Forms
Bosca includes a complete form system for designing, rendering, and collecting structured data. Define forms visually in the admin panel, render them anywhere with a single component, and process submissions with status tracking and notifications.
What you get
- Visual Form Builder: Drag-and-drop editor for designing forms without writing code.
- Dual Schema System: Standard JSON Schema for data validation paired with a Bosca UI Schema for layout and controls — portable across platforms.
- Drop-in Rendering: Render any form with
<BoscaForm schema-key="my-form" />in any Nuxt app. - Extensible Controls: Built-in controls for common field types, plus a registry for adding custom controls.
- Form Submissions: Collect responses with status tracking, profile linking, and review workflows.
- Notifications: Automatic events when submissions arrive.
- Permission Model: Group-based access control on form schemas, consistent with the rest of the platform.
- Analytics Integration: Optional event tracking for form views, field interactions, and submissions.
Two Schema Architecture
Every form in Bosca is defined by a pair of schemas:
| Schema | Purpose | Standard |
|---|---|---|
| JSON Schema | Data types, validation rules, required fields | Draft 2020-12 |
| UI Schema | Layout tree, control types, display elements | Bosca-specific |
This separation keeps the data contract portable — any JSON Schema library can validate form data on any platform — while allowing rich UI customization through the UI Schema. The same schema pair can drive rendering in Vue/Nuxt today and Compose Multiplatform in the future.
JSON Schema
The JSON Schema defines the shape and constraints of the data your form collects:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"name": { "type": "string", "minLength": 1 },
"email": { "type": "string", "format": "email" },
"age": { "type": "integer", "minimum": 0 },
"newsletter": { "type": "boolean" }
},
"required": ["name", "email"]
}
UI Schema
The UI Schema defines how the form looks and behaves — layout containers, field placement, control types, and display elements:
{
"version": 1,
"layout": [
{
"type": "section", "label": "Contact Info",
"children": [
{
"type": "row",
"children": [
{ "type": "field", "property": "name", "control": "text-input", "col": 6 },
{ "type": "field", "property": "email", "control": "text-input", "col": 6 }
]
},
{ "type": "field", "property": "age", "control": "number-input" }
]
},
{
"type": "section", "label": "Preferences",
"children": [
{ "type": "field", "property": "newsletter", "control": "switch" },
{ "type": "display", "control": "help-text", "text": "We'll send updates about once a month." }
]
}
]
}
Layout Nodes
Layout nodes are containers that organize fields and other elements:
| Node | Description |
|---|---|
section | Card wrapper with an optional heading label |
row | 12-column grid row — children specify col (1–12) for width |
tabs | Tabbed container where each child is a separate tab |
Field Nodes
Field nodes bind to a JSON Schema property and render a control:
| Property | Description |
|---|---|
property | Dot-path to the JSON Schema property (e.g. "name", "address.city") |
control | Widget type name from the control registry |
col | Grid column span (1–12) when inside a row |
label | Label override (defaults to property name) |
placeholder | Placeholder text |
rows | Number of rows for textarea controls |
accept | File accept pattern for upload controls |
Display Nodes
Display nodes are visual-only elements that do not collect data:
| Control | Description |
|---|---|
alert | Info, warning, error, or success alert box |
divider | Horizontal line separator |
heading | Section heading text with configurable level |
help-text | Descriptive help text block |
spacer | Vertical spacing |
Form Schemas
A FormSchema is a reusable form definition stored in the backend. Each schema has a unique string key, a name, and the JSON Schema + UI Schema pair.
Schemas are versioned — the version auto-increments each time you save. They support public/published flags and group-based permissions (VIEW, EDIT, DELETE, MANAGE), following the same permission model as content and collections.
Looking up schemas
Schemas can be retrieved by key or ID through the GraphQL API:
query {
formSchemas {
byKey(key: "newsletter-signup") {
id
key
name
schema
uiSchema
version
}
}
}
Built-in Controls
The forms package ships with controls for common field types. Each control maps to a JSON Schema type and renders using Nuxt UI components.
Data Controls
| Control | JSON Schema Type | Widget |
|---|---|---|
text-input | string | Text input |
textarea | string | Multi-line text area |
number-input | integer, number | Number input |
select | string (enum) | Select menu |
checkbox | boolean | Checkbox |
switch | boolean | Toggle switch |
date-picker | string (format: date) | Date picker |
datetime-picker | string (format: date-time) | DateTime picker |
image-upload | string | Image upload with preview |
file-upload | string | File upload |
tag-input | array of strings | Tag/chip input |
radio-group | string (enum) | Radio button group |
color-picker | string | Color picker |
graphql-select | string | Custom select populated via GraphQL query |
api-script-select | string | Custom select populated via API script |
Custom Controls
Register your own controls to extend the form system:
import { registerControl } from '@bosca/forms'
import MyRatingWidget from './MyRatingWidget.vue'
registerControl('star-rating', MyRatingWidget)
Once registered, reference the control by name in any UI Schema field node.
Rendering Forms
The @bosca/forms npm package provides a Vue component for rendering forms in any Nuxt application.
Setup
Install the package and configure it in a Nuxt plugin:
// plugins/02.forms.client.ts
import { setupBoscaForms } from '@bosca/forms'
import { useAuth } from '@bosca/auth-client-browser'
import { useAnalytics } from '@bosca/analytics-client-browser/nuxt'
export default defineNuxtPlugin(() => {
const config = useRuntimeConfig()
const { auth } = useAuth()
const analytics = useAnalytics()
setupBoscaForms({
apiUrl: config.public.apiUrl as string || '',
getToken: () => auth.getStoredToken(),
analytics,
})
})
Auth and analytics are optional — the package works standalone but gains automatic token management and event tracking when they are available.
BoscaForm Component
Render a form by schema key:
<!-- Edit mode: two-way binding for attribute editing -->
<BoscaForm schema-key="org-profile" v-model="attributes" :readonly="!isEditing" />
<!-- Submit mode: public-facing form submission -->
<BoscaForm schema-key="newsletter-signup" mode="submit" submit-label="Sign Up" @submitted="onSuccess" />
<!-- Direct schema (no fetch): useful for builder previews -->
<BoscaForm :schema="jsonSchema" :ui-schema="uiSchema" v-model="data" />
| Prop | Type | Default | Description |
|---|---|---|---|
schemaKey | string | — | Key to auto-fetch schema (mutually exclusive with schema/uiSchema) |
schema | object | — | Pass JSON Schema directly (skip fetch) |
uiSchema | object | — | Pass UI Schema directly (skip fetch) |
modelValue | object | {} | Two-way binding for form data |
readonly | boolean | false | Disable all editing |
errors | object | — | External validation errors |
mode | 'edit' | 'submit' | 'edit' | Edit = two-way binding; Submit = collect and submit |
submitLabel | string | 'Submit' | Button text in submit mode |
formSchemaId | string | — | FormSchema ID for submission mode |
Events: update:modelValue, validate, submitted
Validation
Form data is validated against the JSON Schema using AJV with format support. Validation runs on field blur and on form submission. Per-property errors are mapped back to the corresponding field for inline display.
import { validateFormData, validateField } from '@bosca/forms'
// Validate entire form
const errors = validateFormData(schema, formData)
// Validate a single field
const error = validateField(schema, 'email', value)
Form Submissions
Form submissions let you collect responses from users. Each submission records the submitted data, links to a profile (optional for anonymous submissions), and tracks a status through a review lifecycle.
Submission Status
| Status | Description |
|---|---|
PENDING | New submission awaiting review |
PROCESSED | Reviewed and accepted |
REJECTED | Reviewed and declined |
SPAM | Flagged as spam |
ARCHIVED | Moved to archive after processing |
How Submissions Work
- A user submits a form by providing attribute values and an optional profile reference.
- Bosca validates the submission against the template and stores it with
PENDINGstatus. - A notification event fires automatically, alerting configured recipients.
- Reviewers update the submission status as they process it.
Anonymous Submissions
Submissions work without authentication. Anonymous submissions are rate-limited to 10 per minute per IP address to prevent abuse.
Authenticated Submissions
When the auth integration is configured, submissions automatically include the user's Bearer token and link to their profile.
Form Templates
A FormTemplate defines form structure using the content system's attribute model. Each template includes:
- Attributes — the fields your form collects (labels, types, validation)
- Default Values — pre-filled attribute values applied when the form renders
- Configuration — notification recipients, rate limiting, and operational settings
- Versioning — templates track versions so you can evolve forms over time
Form templates can reference a form schema key in their configuration, bridging the two systems — when present, rendering uses <BoscaForm> with the referenced schema instead of attribute-based rendering.
Analytics Integration
When analytics is configured, the <BoscaForm> component automatically tracks user interactions:
| Event | Type | Description |
|---|---|---|
form.view | impression | Form was displayed to the user |
form.field.interaction | interaction | User focused on a form field |
form.validation.error | error | Validation failed for a field |
form.submit | completion | Form was successfully submitted |
form.submit.error | error | Submission failed |
These events flow through the standard analytics pipeline and are available for querying and dashboards.
Form Builder
The Administration UI includes a visual form builder for designing forms without writing code.
Builder Workflow
- Field Palette — drag controls from a categorized palette (data fields, layout, display elements) onto the canvas.
- Layout Canvas — arrange and nest nodes in the layout tree with drag-and-drop sorting.
- Node Editor — configure properties of the selected node (labels, validation rules, control type, column widths).
- Live Preview — see the form rendered in real time as you build it.
- Save — persist the form schema by key (auto-increments version on updates).
Administration
The admin panel provides:
- Schema List — browse, search, and manage all form schemas
- Template Editor — create and configure form templates
- Submissions Page — review, filter, and manage incoming submissions
- Status Management — update submission statuses individually or in bulk
- Permission Controls — restrict who can view, edit, or manage form schemas
For developers
Related modules:
- Core interfaces:
backend/framework/core-forms - Implementation:
backend/framework/forms - GraphQL schema:
backend/framework/forms/src/main/resources/graphql/forms.graphqls - npm package:
web/forms(@bosca/forms) - Specification:
specs/forms/spec.md
Related:
- Profiles: User profiles
- Notifications: Messaging
- Workflows: Workflow automation
- Analytics: Event tracking
- Identity: Authentication