Forms

Build, render, and collect structured data with JSON Schema-based forms, a visual builder, and a cross-platform rendering engine.

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:

SchemaPurposeStandard
JSON SchemaData types, validation rules, required fieldsDraft 2020-12
UI SchemaLayout tree, control types, display elementsBosca-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:

NodeDescription
sectionCard wrapper with an optional heading label
row12-column grid row — children specify col (1–12) for width
tabsTabbed container where each child is a separate tab

Field Nodes

Field nodes bind to a JSON Schema property and render a control:

PropertyDescription
propertyDot-path to the JSON Schema property (e.g. "name", "address.city")
controlWidget type name from the control registry
colGrid column span (1–12) when inside a row
labelLabel override (defaults to property name)
placeholderPlaceholder text
rowsNumber of rows for textarea controls
acceptFile accept pattern for upload controls

Display Nodes

Display nodes are visual-only elements that do not collect data:

ControlDescription
alertInfo, warning, error, or success alert box
dividerHorizontal line separator
headingSection heading text with configurable level
help-textDescriptive help text block
spacerVertical 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

ControlJSON Schema TypeWidget
text-inputstringText input
textareastringMulti-line text area
number-inputinteger, numberNumber input
selectstring (enum)Select menu
checkboxbooleanCheckbox
switchbooleanToggle switch
date-pickerstring (format: date)Date picker
datetime-pickerstring (format: date-time)DateTime picker
image-uploadstringImage upload with preview
file-uploadstringFile upload
tag-inputarray of stringsTag/chip input
radio-groupstring (enum)Radio button group
color-pickerstringColor picker
graphql-selectstringCustom select populated via GraphQL query
api-script-selectstringCustom 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" />
PropTypeDefaultDescription
schemaKeystringKey to auto-fetch schema (mutually exclusive with schema/uiSchema)
schemaobjectPass JSON Schema directly (skip fetch)
uiSchemaobjectPass UI Schema directly (skip fetch)
modelValueobject{}Two-way binding for form data
readonlybooleanfalseDisable all editing
errorsobjectExternal validation errors
mode'edit' | 'submit''edit'Edit = two-way binding; Submit = collect and submit
submitLabelstring'Submit'Button text in submit mode
formSchemaIdstringFormSchema 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

StatusDescription
PENDINGNew submission awaiting review
PROCESSEDReviewed and accepted
REJECTEDReviewed and declined
SPAMFlagged as spam
ARCHIVEDMoved to archive after processing

How Submissions Work

  1. A user submits a form by providing attribute values and an optional profile reference.
  2. Bosca validates the submission against the template and stores it with PENDING status.
  3. A notification event fires automatically, alerting configured recipients.
  4. 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:

EventTypeDescription
form.viewimpressionForm was displayed to the user
form.field.interactioninteractionUser focused on a form field
form.validation.errorerrorValidation failed for a field
form.submitcompletionForm was successfully submitted
form.submit.errorerrorSubmission 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

  1. Field Palette — drag controls from a categorized palette (data fields, layout, display elements) onto the canvas.
  2. Layout Canvas — arrange and nest nodes in the layout tree with drag-and-drop sorting.
  3. Node Editor — configure properties of the selected node (labels, validation rules, control type, column widths).
  4. Live Preview — see the form rendered in real time as you build it.
  5. 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: