Installation¶
KForm consists of:
- A Kotlin Mutliplatform core library, published to Maven Central as
tech.ostack:kform; - A Kotlin/JS library providing bindings for usage from JavaScript, published to Maven Central as
tech.ostack:kform-js-bindingsand to npmjs as@ostack.tech/kform; - A React library for using KForm together with React, published to npmjs as
@ostack.tech/kform-react.
A typical project using KForm contains three modules:
- A shared Kotlin Multiplatform module, containing the definition of the form, compiling to both JVM and JavaScript.
- The React client module, consuming the shared module’s JavaScript target and using KForm to render and manage the form.
- The JVM server module, consuming the shared module’s JVM target and using KForm to validate submitted forms.
Let us assume that all three modules are within the same Gradle build with paths :shared,
:react-app, and :server.
Add the Maven Central repository to your Gradle build if not already present:
Shared/KMP module¶
Add the core dependency to your :shared Kotlin Multiplatform project’s commonMain dependencies,
and the JavaScript bindings to the jsMain dependencies. JavaScript compilation should target
ES2015 and represent Longs as bigints (which currently requires the -Xes-long-as-bigint
compiler option):
plugins {
kotlin("multiplatform")
// …
}
kotlin {
// …
jvm { testRuns["test"].executionTask.configure { useJUnitPlatform() } }
js {
compilerOptions {
target = "es2015"
freeCompilerArgs.add("-Xes-long-as-bigint")
}
binaries.library()
browser()
generateTypeScriptDefinitions()
}
sourceSets {
commonMain.dependencies {
api("tech.ostack:kform:0.33.0")
// …
}
jsMain.dependencies {
implementation("tech.ostack:kform-js-bindings:0.33.0")
// …
}
// …
}
}
For consuming the shared module in your React application, you might want to create a task to pack
the compiled JavaScript. The following example uses the
node-gradle plugin to achieve this:
plugins {
// …
id("com.github.node-gradle.node") version "…"
}
val packJsPackage by
tasks.registering(NpmTask::class) {
group = "build"
dependsOn("jsBrowserProductionLibraryDistribution")
val libraryDir = "build/dist/js/productionLibrary"
npmCommand = listOf("pack", libraryDir, "--pack-destination", "build")
inputs.dir(libraryDir)
outputs.file("build/your-form-shared-$version.tgz")
}
Note
your-form should typically be replaced with the name of your root Gradle project.
Client/React application module¶
These instructions will be using Vite as the build tool for the React
application in conjunction with Gradle via
node-gradle. Other frontend bundling tools
should work similarly.
Configure Gradle to consume the shared module’s JavaScript target and set up tasks to develop and build the React application:
plugins {
base
id("com.github.node-gradle.node") version "…"
}
val sharedPackage = project(":shared").file("build/your-form-shared-$version.tgz")
val npmInstallShared by
tasks.registering(NpmTask::class) {
group = "build"
dependsOn(":shared:packJsPackage")
npmCommand = listOf("install", "your-form-shared@file:$sharedPackage")
inputs.file(sharedPackage)
outputs.dir("node_modules/your-form-shared")
}
tasks.npmInstall { dependsOn(npmInstallShared) }
val npmRunDev by
tasks.registering(NpmTask::class) {
group = "development"
dependsOn(tasks.npmInstall)
npmCommand = listOf("run", "dev")
}
val npmRunBuild by
tasks.registering(NpmTask::class) {
group = "build"
dependsOn(tasks.npmInstall)
npmCommand = listOf("run", "build")
inputs.dir("src")
inputs.file(sharedPackage)
inputs.file("index.html")
inputs.file("package.json")
inputs.file("tsconfig.json")
// …
inputs.file("vite.config.ts")
outputs.dir("dist/")
}
tasks.assemble { dependsOn(npmRunBuild) }
Due to how Kotlin Multiplatform compiles code to JavaScript, consuming multiple Kotlin/JS modules
from JavaScript can be a bit tricky. As such, we won’t use the published @ostack.tech/kform
package from npmjs and instead consume it from the :shared module directly. We still install
@ostack.tech/kform for its type definitions by installing it “renamed” as
@types/ostack.tech__kform:
{
// …
"scripts": {
"dev": "vite",
"build": "tsc -b && vite build"
// …
},
"dependencies": {
"@ostack.tech/kform-react": "^0.33.0",
"react": "…",
"react-dom": "…"
// …
},
"devDependencies": {
"@types/ostack.tech__kform": "npm:@ostack.tech/kform@^0.33.0",
"@types/react": "…",
"@types/react-dom": "…",
"@vitejs/plugin-react": "…",
"typescript": "…",
"vite": "…"
// …
}
}
To consume @ostack.tech/kform from the :shared module, we use a Vite alias targetting the
ostack-kform.mjs file, like so:
import { resolve } from "node:path";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
"@ostack.tech/kform": resolve(
import.meta.dirname,
"node_modules/your-form-shared/ostack-kform.mjs",
),
},
},
});
Server/JVM module¶
In your server module, add the dependency to your shared KMP module (if exposing the core KForm
library by declaring the dependency via api):
If required, you may also add a dependency to KForm’s core JVM module directly:
You can add a task to copy the built React application to the server’s resources. E.g. for a Spring Boot application: