Skip to content

Repository files navigation

Freeform Headless React Demo

Example Vite + React app that renders Solspace Freeform forms using the published @solspace/freeform-* packages from npm.

Official packages:

Package Role
@solspace/freeform-core Manifest, state, submit
@solspace/freeform-react <Freeform /> and useFreeform()
@solspace/freeform-extensions Captchas, calculation, datetime, file drag & drop, table, signature
@solspace/freeform-theme-default Default light / dark theme
@solspace/freeform-theme-tailwind Official Tailwind starter (class maps, no CSS)
@solspace/freeform-theme-bootstrap Official Bootstrap 5 starter (class maps, no CSS)

What you need

Before running this app:

  1. A Craft CMS site with Freeform installed (version that includes the headless REST API)
  2. A Freeform form you want to display (you’ll use its handle, e.g. contact)
  3. Node.js 20+

1. Enable headless Freeform (on your Craft site)

This step is done in your Craft CMS project, not in this React demo repo.

Headless must be turned on in Freeform before this app can load a form.

  1. Open your Craft site project (the CMS install — e.g. a DDEV project, not this Vite app).

  2. Edit (or create) the Freeform config file at:

    your-craft-project/config/freeform.php
    

    Typical locations:

    Setup Path
    Standard Craft config/freeform.php at the Craft project root
    DDEV / local same — e.g. ~/Sites/my-site/config/freeform.php
  3. Merge a headless section into that file (keep any other Freeform settings you already have). See the Freeform Headless docs for the full options:

<?php
// your-craft-project/config/freeform.php

return [
    'headless' => [
        'enabled' => true,
        'forms' => [
            'contact' => [          // ← your Freeform form handle
                'exposeManifest' => true,
                'allowSubmit' => true,
            ],
        ],
    ],
];

Tips:

  • Replace contact with the form handle from the Freeform control panel (Forms → your form).
  • For public forms, enable a captcha in Freeform.
  • Keep Craft running (e.g. DDEV at https://site.ddev.site) — you will point CRAFT_PROXY_TARGET at that URL in step 2.

2. Clone and configure this app

git clone https://github.com/solspace/freeform-headless-react-demo.git
cd freeform-headless-react-demo

cp .env.example .env

Edit .env:

# Craft site URL (Vite proxies /freeform and /actions here)
CRAFT_PROXY_TARGET=https://your-site.example

# Form handle from Freeform (same as in step 1)
VITE_FREEFORM_HANDLE=contact

# Optional — GraphQL tab
VITE_GRAPHQL_PATH=/actions/graphql/api
VITE_GRAPHQL_TOKEN=your-craft-graphql-token
Variable Purpose
CRAFT_PROXY_TARGET Your Craft / Freeform site URL
VITE_FREEFORM_HANDLE Default form handle shown when the app starts
VITE_GRAPHQL_PATH Craft GraphQL endpoint (default /actions/graphql/api)
VITE_GRAPHQL_TOKEN Craft GraphQL schema token (needed for the GraphQL tab)

3. Install and run

pnpm install
pnpm dev

Open http://localhost:3000.

How it connects: the React app calls /freeform/... on localhost. Vite proxies those requests to CRAFT_PROXY_TARGET, so CSRF cookies work on the same origin.

Use npm or yarn if you prefer. This demo’s package.json pins @solspace/freeform-* from the registry.

How to use a form in this app

Load your form

  1. Make sure the form is headless-enabled (step 1).
  2. In the app Form settings, enter the form handle → Load form.
    Or set VITE_FREEFORM_HANDLE in .env and restart pnpm dev.

Try the demo modes

1. Pick an API (top tabs)

Tab What it uses
REST /freeform headless endpoints (recommended default)
GraphQL Craft GraphQL adapters via fetch={graphqlFetch}

2. Pick a view (second row)

Tab What it shows
<Freeform /> Full form with the default theme
useFreeform() Headless hook — you own the markup
Manifest JSON Raw manifest (REST or GraphQL depending on the API tab)

GraphQL: create a Craft GraphQL schema with Freeform form read + submission create, enable your site, and paste a token into VITE_GRAPHQL_TOKEN. Pass the demo’s graphqlFetch helper as fetch:

import { Freeform } from "@solspace/freeform-react";
import { graphqlFetch } from "./graphqlFetch"; // see this repo

<Freeform
  handle="contact"
  baseUrl={window.location.origin}
  fetch={graphqlFetch}
  extensions={recommendedExtensions}
/>

JSON submits use GraphQL; multipart file uploads still go through REST.

Save & Continue: if the form has Save enabled, click Save — the URL gets ?session-token=…&key=…. Refresh or share that link to resume. See React docs → Save & Continue Later.

Advanced fields: recommendedExtensions covers captchas, datetime, file drag & drop, calculation, table, and signature.

Use the same pattern in your own project

import { Freeform } from "@solspace/freeform-react";
import { recommendedExtensions } from "@solspace/freeform-extensions";
import "@solspace/freeform-theme-default/styles.css";

export function ContactForm() {
  return (
    <Freeform
      handle="contact"
      baseUrl={window.location.origin}
      extensions={recommendedExtensions}
    />
  );
}

When /freeform is proxied (or served) on the same host as your frontend, keep baseUrl as your app’s origin.

Scripts

Command Description
pnpm dev Dev server
pnpm build Production build
pnpm preview Preview the production build

Custom port: PORT=3001 pnpm dev

Next.js

The same packages work in Next.js. Use a Client Component and rewrite /freeform to your Craft site (same idea as this Vite proxy). See Solspace Freeform → Headless docs.

Docs

Troubleshooting

Issue Fix
Form won’t load / 404 Wrong handle, or headless not enabled for that form (step 1).
CSRF / session errors Keep using the Vite proxy; don’t call Craft from another origin without CORS + credentials.
CORS errors Prefer the proxy, or add http://localhost:3000 to headless.allowedOrigins.
Captcha / file upload missing Enable those integrations in Freeform; this demo already loads recommendedExtensions.
Save & Continue Enable Save on the form’s Button Layout. After Save, this demo puts session-token + key in the URL — refresh to resume.

License

This demo is MIT. The Freeform Craft plugin is licensed separately — see Solspace Freeform.

About

Public example app: [Vite + React] rendering Craft Freeform forms with Solspace’s official packages from the npm registry.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages