This React component turns a headless CMS into a form builder

30 Nov 2020

Many CMSs and SaaS services will let non-technical users build forms to their requirements for data collection. While headless CMSs often power the content of React applications, this form builder functionality is not something really associated with React. This article shows you how you can create a React application with a form whose structure is data driven and can be managed by non-technical users through a CMS interface. To do this, we will use tools which are part of the Restspace project.

The first thing we need is a means to build a form in React whose structure is data driven. We will use Restspace's Schema Form package on NPM. This package contains a component which renders a form whose structure and validation is determined by a JSON Schema. It's highly capable and can render a wide range of components including custom React components, and generate structured forms with lists and subforms, conditional fields etc. and even recursive forms.

Any headless CMS which is capable of storing a JSON Schema object is capable of supplying the form definition schema. We're going to use Restspace itself to do this. Restspace uses JSON Schema as the structure definition for its data store, and the schema is available via Restspace's API. The application containing the form can therefore read the schema from the API, allowing it to act as a single source of truth. The schema can be edited via a form-based interface in Restspace's admin UI, so non-technical users can do this.

In addition, Restspace allows non-technical users to view and edit submitted form data via a form constructed using components from the Schema Form package in its admin UI. This makes a great setup for management of user-entered data such as an onboarding process.

Now we'll describe the steps involved in building this.

Create the React project

Create a React project using create-react-app in the usual way:

npx create-react-app json-schema-form-demo
or npx create-react-app json-schema-form-demo --template typescript

We're going to use Typescript in this article, it should be easy enough to take out the types to get vanilla JS if that's what you prefer. The next step is to install the Restspace Schema Form package:

npm install @restspace/schema-form

Now create a form with a preset JSON Schema, to see it working without being connected to an API. Replace the contents of App.tsx with the code below:

import React from 'react';
import "@restspace/schema-form/build/index.css";
import "@restspace/schema-form/src/css/default-skin.css";
import { SchemaSubmitForm } from '@restspace/schema-form';

function App() {
  const schema = {
    type: "object",
    properties: {
      "firstName": { type: "string" },
      "lastName": { type: "string" }
  const value = {};

  const onDoSubmit = async (val: object) => {
    alert(JSON.stringify(val, null, 4));
    return true;

  return (
    <div className="App">
      <SchemaSubmitForm {...{ schema, value }}
          makeSubmitLink={(onClick) => (
            <button type="button" className='sf-submit-button'

export default App;

Tip: if you're using VS Code and see a syntax error marked, it's probably because your Typescript versions are out of sync. Click CTRL + SHIFT + P and select 'Typescript: Select Typescript Version'. Choose 'Use Workspace Version' and the problem should go away.

The variable schema is a JSON schema defining the form structure. The onDoSubmit function handles the user's click of the submit button, receiving the current form value as an object with properties for the field values. The makeSubmitLink prop is set to a function that when given a handler for a click of the button, returns a React element to be used to render the submit button. SchemaSubmitForm is the component which renders the form.

This demonstrates how the form works without it being connected to a source for the schema.

Set up Restspace

Now go to the Restspace home page, click the GET YOUR INSTANCE button a little way down the page, and fill in the short registration form, choosing a unique subdomain for your Restspace account. Within a couple of minutes, you'll receive a confirmation email (remember to check your Junk folder!). Click the 'Activate Account' link to return to the Restspace site, you'll see this page:

Click the top link for the Admin interface. Log in with the email and password you gave at registration. You'll see the admin UI. Expand the JSON section on the left and click the folder-shaped '+' button.

Enter a name for the new directory, we'll choose 'schema-form' and hit return. Then click on the 'schema-form' directory heading which appears and you'll see this appear on the right for entering the schema for the data in this directory:

You can create the schema by using a form in the tab you can see open on the right. If you click the second tab, it will give you a text editor into which you can enter any JSON schema. The form editor is somewhat limited in the schemas it will let you create. But it will be fine for this demo. Click the '+' button twice to add two fields and fill them out as shown below, then click SUBMIT.

You've now created a schema which governs the structure of JSON files to be stored in this directory. You can read this schema from the url /json/schema-form/schema.config.json.

Integrating front-end and back-end

Now we'll go back to the React project and amend it to read the schema:

const restspaceBase = '';

function App() {
  const [schema, setSchema] = useState({} as object);

  useEffect(() => {
    (async () => {
      const resp = await fetch(restspaceBase +
      if (resp.ok) {
        const newSchema = await resp.json();
  }, []);

  const value = {};

We add hooks to the App component to store the schema as state, and to initially run a request to fetch the schema from Restspace. Make sure to change the subdomain of restspaceBase to the subdomain you created. Also you'll need to add useEffect and useState to the import for React.

You can run this and it should show the same form it did before.

Now let's add a bit more code to send the entered data to Restspace. We replace the part which sets the const onDoSubmit:

const onDoSubmit = async (val: object) => {
  const entryTimestamp = new Date().toISOString();
  await fetch(restspaceBase + 'json/schema-form/' + entryTimestamp, {
    method: 'PUT',
    body: JSON.stringify(val),
    headers: {
      "Content-Type": "application/json"
  return true;

This makes a PUT request to the schema-form folder with the entered data using a resource name made from the current time. This means form data will be listed in created time order. For this to work, we also need to loosen permissions in Restspace to allow unauthenticated writing. Go back to the Restspace admin screen and click the grey bar at the top left titled with the subdomain of your Restspace site. You'll see the service list:

Now click the edit button (pencil) on the top right of the panel for the JSONSERVICE. This opens the service editor panel. In the Write roles field add /schema-form all to enable just the schema-form directory to be writable by all users.

Now click SUBMIT then click the Commit Changes button on the top right. Click OK to the confirmation and the page will refresh with the new settings. You can now go back to the React application and try entering data into the form and saving it.

Enter values in the fields and click SUBMIT. Now go back to Restspace. Click the expander beside the schema-form item in the directory tree on the left twice to close and open it, which will refresh the listing of the directory.

Now let's change the form from Restspace's admin UI. Click on the schema-form item in the tree navigation on the left. In the fields form, we are going to add the 'title' item shown. Notice in this case, you have a static, limited number of items in the drop down which you supply as a bar-separated list. Click the plus at the bottom of the form, fill in the fields, and use the up-arrow button to move it to the top. Then click SUBMIT to update the schema.

Then return to the React project, refresh the page and you'll see the form update.

Exploring further

To learn more about how the schema-form components work and how they support JSON Schema see the ReadMe at

You can host the React application you just created using Restspace. Create a static site hosting service by clicking the grey bar at the top left of the admin site, clicking Add Service at the top left of the main pane, then scrolling down the service catalogue that appears to the right, until you find StaticSite. Click the block and you then get a form to fill in the configuration. Below is an example for a creating a React site on the root path:

Click SUBMIT - the static site will be created at the bottom of the list of services which it will only handle urls which don't match any of the others above it. Then click Commit Changes on the top right to create the static site service. Once this is done, when you click on the name of the static site service ('Site') in the navigator on the left, you'll get a page where you can upload the site.

To do this, build the React site and make a zip including all the top level files and directories in the build or dist directory. Then upload this zip to the static site service and it will be deployed to your Restspace domain. Go to the homepage of your domain and you will now be able to see the form.