Usage
This page demonstrates how to use the jsonforms-kotlin
library to render dynamic forms in your
Kotlin Multiplatform projects. It covers basic setup, state management, the main JsonForm
component, and renderer usage.
Your schemas
Before rendering a form, define your JSON Schema and UI Schema.
Tip: Both the schema and uischema models are serializable, so you can initialize them from a backend service or load them dynamically at runtime.
val schema = Schema(
properties = persistentMapOf(
"email" to StringProperty(
pattern = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
),
"password" to StringProperty()
),
required = persistentListOf("email", "password")
)
val uiSchema = VerticalLayout(
elements = persistentListOf(
Control(
scope = "#/properties/email",
label = "Email"
),
Control(
scope = "#/properties/password",
label = "Password",
options = ControlOptions(format = Format.Password)
)
)
)
Create and manage form state
The JsonFormState
manages your form's data, validation, and error states. You create it using the
rememberJsonFormState
composable, which takes a map of initial field values:
val formState = rememberJsonFormState(
initialValues = mutableMapOf(
"email" to "",
"password" to ""
)
)
Tip: You can also provide an empty map (
mutableMapOf()
) to initialize the form with empty values for all fields defined in your schema. The form will automatically handle the fields based on your schema and uischema definitions.
You can observe and update the form state as the user interacts with the form. Validation and error handling are integrated for use with your schemas.
Rendering
You can use any renderer (Material3, Cupertino, or your own custom renderer) on any platform.
To render a form, use the JsonForm
component and provide the appropriate layout and field
renderers for your design system.
Here are examples for both Material3 and Cupertino renderers:
// Material3 rendering
JsonForm(
schema = schema,
uiSchema = uiSchema,
state = formState,
layoutContent = { Material3Layout(content = it) },
stringContent = { id ->
val value = formState[id].value as String?
val error = formState.error(id = id).value
Material3StringProperty(
value = value,
error = error?.message,
onValueChange = { formState[id] = it }
)
},
// Add numberContent, booleanContent, etc. as needed
)
// Cupertino rendering
JsonForm(
schema = schema,
uiSchema = uiSchema,
state = formState,
layoutContent = { CupertinoLayout(content = it) },
stringContent = { id ->
val value = formState[id].value as String?
val error = formState.error(id = id).value
CupertinoStringProperty(
value = value,
error = error?.message,
onValueChange = { formState[id] = it }
)
},
// Add numberContent, booleanContent, etc. as needed
)
You can mix and match renderers or create your own by providing custom implementations for the layout and field content slots. All renderers are available on all supported platforms.
Accessing form data
To access the current data entered in the form, use the getData()
method on your form state.
This returns a map where each key is a field name and the value is the current user input for that
field:
val data: Map<String, Any?> = formState.getData()
You can call this method at any time to retrieve the latest values from the form. This is useful for submitting the form data to a backend or for further processing in your application.
Validating form data
To validate the form content according to the rules described in your schema and uischema, use
the validate
suspend function on your form state. This function checks all requirements
(such as required fields, patterns, and other constraints) and updates the error state for each
field:
import kotlinx.coroutines.launch
val scope = rememberCoroutineScope()
Button(onClick = {
scope.launch {
val isValid = formState.validate(schema, uiSchema)
// isValid is true if all requirements and patterns are respected
// You can use this to control form submission
}
}) {
Text("Validate")
}
- The
validate
function is suspendable and should be called from a coroutine (as shown above). - After calling
validate
, the form will update its error states, and you can access error messages for each field usingformState.error(id).value
. - The function returns
true
if the form is valid, orfalse
if there are validation errors.
This approach ensures that your form always respects the schema and uischema rules before submission or further processing.