Skip to main content

Quickstart: Build a React Form from JSON

This quickstart gets you to a working FormEngine form fast. By the end, you will have a React form rendered from JSON, with validation and a clean starting point for adding dynamic behavior later. The quickstart flow looks like this: Quickstart example

What you are building

You will:
  • install FormEngine Core and an RSuite-based component library
  • define a simple contact form in JSON
  • render it in React with FormViewer
  • inspect form data and validation output
If you already have a React app, you can copy these steps directly into it.

Prerequisites

  • Node.js 18+
  • a React project using Vite, Next.js, Remix, or a similar setup
If you do not have a React project yet, create one with npm create vite@latest my-form-app -- --template react-ts, then run cd my-form-app and npm install.

Step 1: Install the packages

Install FormEngine Core and the RSuite component package:
npm install @react-form-builder/core @react-form-builder/components-rsuite
This gives you the runtime plus a ready-made UI layer for rendering the form.

Step 2: Create a JSON form definition

Create a file named contactForm.json:
contactForm.json
{
  "form": {
    "key": "Screen",
    "type": "Screen",
    "children": [
      {
        "key": "name",
        "type": "RsInput",
        "props": {
          "label": { "value": "Your name" },
          "placeholder": { "value": "Enter your full name" }
        },
        "schema": {
          "validations": [
            { "key": "required", "args": { "message": "Name is required" } }
          ]
        }
      },
      {
        "key": "email",
        "type": "RsInput",
        "props": {
          "label": { "value": "Email" },
          "placeholder": { "value": "you@example.com" }
        },
        "schema": {
          "validations": [
            { "key": "required", "args": { "message": "Email is required" } },
            { "key": "email", "args": { "message": "Enter a valid email" } }
          ]
        }
      },
      {
        "key": "message",
        "type": "RsTextArea",
        "props": {
          "label": { "value": "Message" },
          "rows": { "value": 4 }
        }
      }
    ]
  },
  "errorType": "RsErrorMessage"
}
This schema gives you three fields:
  • a required name field
  • an email field with validation
  • an optional message field
The key point is that the form structure and validation now live in JSON, not in a custom JSX field tree.

Step 3: Render the form in React

Now render that JSON with FormViewer:
App.tsx
import { useCallback } from 'react'
import { FormViewer } from '@react-form-builder/core'
import { rsView, formEngineRsuiteCssLoader } from '@react-form-builder/components-rsuite'
import contactForm from './contactForm.json'

formEngineRsuiteCssLoader()

export default function App() {
  const getForm = useCallback(() => JSON.stringify(contactForm), [])

  const handleDataChange = useCallback((formData) => {
    console.log('Form data:', formData.data)
    console.log('Errors:', formData.errors)
  }, [])

  return (
    <FormViewer
      view={rsView}
      getForm={getForm}
      onFormDataChange={handleDataChange}
    />
  )
}
Run your dev server and open the app. You should now see a working form with live validation.

Step 4: Understand what just happened

At this point, you already have the core FormEngine model working:
  • the form structure is defined in JSON
  • the viewer renders it with a component library
  • validation rules run from the schema
  • form data changes are available to your app
That is the main reason teams use FormEngine. As forms get larger, the schema-first approach is easier to reuse and evolve than hand-wiring every field in React.

Step 5: Add submission logic

When you are ready to submit the form, use a viewer ref and read the current data:
App.tsx
import { useCallback, useRef } from 'react'
import { FormViewer, IFormViewer } from '@react-form-builder/core'
import { rsView, formEngineRsuiteCssLoader } from '@react-form-builder/components-rsuite'
import contactForm from './contactForm.json'

formEngineRsuiteCssLoader()

export default function App() {
  const viewerRef = useRef<IFormViewer>(null)
  const getForm = useCallback(() => JSON.stringify(contactForm), [])

  const handleSubmit = useCallback(async () => {
    const viewer = viewerRef.current
    if (!viewer) return

    const { data, errors } = viewer.getFormData()

    if (Object.keys(errors).length > 0) {
      console.log('Validation errors:', errors)
      return
    }

    await fetch('/api/contact', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    })
  }, [])

  return (
    <div>
      <FormViewer
        view={rsView}
        getForm={getForm}
        viewerRef={viewerRef}
      />
      <button onClick={handleSubmit}>Submit</button>
    </div>
  )
}

Common next improvements

After this quickstart, most teams add one of these next:

FAQ

No. This quickstart uses FormEngine Core only. You can define the form directly in JSON and render it without the visual builder.
Because JSON makes the form structure easier to reuse, version, and extend as workflows become more dynamic.
Yes. RSuite is just the quickest path for this example. FormEngine also supports other UI libraries and custom components.

Next steps

Understand validation

Add richer rules, error handling, and cross-field checks.

Add conditional logic

Show and hide fields based on user input.

Build a multi-step form

Turn a simple form into a wizard flow with step-based progression.

Open the Online Builder

Create a form visually and export the schema.