Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions craft-freeform/headless/getting-started.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
title: Getting Started
sidebar_position: 1
description: Install and enable Freeform's official headless React packages for Craft CMS.
---

import { Badge, BadgeGroup } from '@site/src/components/utils';
import { VerticalStepWrapper, StepMarkdown } from '@site/src/components/docs';

# Getting Started (React Headless)<BadgeGroup><Badge type="lite" text="Lite" /><Badge type="pro" text="Pro" /></BadgeGroup>

Freeform’s recommended headless path for React and Next.js is:

1. Use a Freeform plugin build that includes the **headless REST API**
2. Enable headless in Freeform config
3. Install the official npm packages
4. Render forms with `<Freeform />` or build your own UI with `useFreeform()`

GraphQL and older AJAX demos remain available, but new React/Next.js projects should start here.

:::tip Example app
Clone the [Freeform Headless React Demo](https://github.com/solspace/freeform-headless-react-demo) to try the packages against your Craft site (REST, GraphQL tab, Save & Continue, table, signature, calculation, and more).
:::

## Requirements

| Piece | Version / notes |
| --- | --- |
| Craft CMS | 4.17+ or 5.9+ |
| Freeform plugin | Build that includes headless REST |
| npm packages | Official `@solspace/freeform-*` packages ([npm org](https://www.npmjs.com/org/solspace)) |
| React | 18 or 19 |
| Node | Modern LTS with `npm` / `pnpm` / `yarn` |

## Quick setup

<VerticalStepWrapper>
<StepMarkdown stepTitle="Enable headless in Freeform (Craft project)">

Headless is **off by default**. Configure it in your **Craft CMS project** (the site where Freeform is installed) — not in your React / Next.js app repo.

Edit or create:

```text
your-craft-project/config/freeform.php
```

That file lives next to Craft’s other config files (`general.php`, `routes.php`, etc.), for example:

```text
~/Sites/my-craft-site/config/freeform.php
```

Merge a `headless` section into that file (keep any existing Freeform settings):

```php title="config/freeform.php" showLineNumbers
<?php

return [
'headless' => [
// Master switch — endpoints return 404 when false
'enabled' => true,

// CORS origins for browser apps that call Craft directly
'allowedOrigins' => [
'http://localhost:3000',
'https://app.example.com',
],

// Per-form exposure (keyed by form handle from the Freeform CP)
'forms' => [
'contact' => [
'exposeManifest' => true,
'allowSubmit' => true,
],
],
],
];
```

:::tip Same-origin proxy
If your frontend proxies `/freeform/*` to Craft (recommended for Next.js), CSRF cookies stay same-origin and you can keep `allowedOrigins` tight.
:::

See the full [REST API](./rest-api.mdx) reference for profiles, CSRF, and submit details.

</StepMarkdown>
<StepMarkdown stepTitle="Install the npm packages">

```bash
npm install @solspace/freeform-core \
@solspace/freeform-react \
@solspace/freeform-extensions \
@solspace/freeform-react-theme-default
```

| Package | Purpose |
| --- | --- |
| `@solspace/freeform-core` | Manifest client, form state, conditionals, submit |
| `@solspace/freeform-react` | `<Freeform />` component and `useFreeform()` hook |
| `@solspace/freeform-extensions` | Captchas, datetime, file drag & drop, calculation, table, signature |
| `@solspace/freeform-react-theme-default` | Default light/dark theme CSS |

</StepMarkdown>
<StepMarkdown stepTitle="Render a form">

```tsx title="ContactForm.tsx" showLineNumbers
import { Freeform } from '@solspace/freeform-react';
import { recommendedExtensions } from '@solspace/freeform-extensions';
import '@solspace/freeform-react-theme-default/styles.css';

export function ContactForm() {
return (
<Freeform
handle="contact"
baseUrl="https://cms.example.com"
extensions={recommendedExtensions}
onSuccess={(response) => {
console.log('Submitted', response);
}}
/>
);
}
```

- **`handle`** — Freeform form handle (must be enabled under `headless.forms`)
- **`baseUrl`** — Craft site origin, **or** `""` / same origin when you proxy `/freeform`
- **`extensions`** — register captchas and advanced fields when the form needs them

</StepMarkdown>
</VerticalStepWrapper>

## Choose your guide

| Guide | When to use it |
| --- | --- |
| [React JS](./reactjs.mdx) | Vite, CRA, Remix, or any React SPA |
| [Next.js](./nextjs.mdx) | App Router / Pages Router + proxy |
| [REST API](./rest-api.mdx) | Endpoints, CSRF, CORS, profiles |
| [GraphQL](./graphql.mdx) | Headless adapters (`freeformHeadlessManifest` / `freeformHeadlessSubmit`) or legacy form GraphQL |
| [Example demo](https://github.com/solspace/freeform-headless-react-demo) | Cloneable Vite app (REST + GraphQL tab) |

## What’s included

- Manifest load + CSRF + submit (JSON and multipart)
- Multi-page forms and conditionals (client UX)
- Captchas via `@solspace/freeform-extensions`
- File upload and File Drag & Drop
- **Save & Continue Later** (draft token + key; see [React JS](./reactjs.mdx#save--continue-later))
- Calculation fields
- Table fields (row limits, required columns, file cells)
- Signature fields (canvas pad + clear)
- Default theme with light / dark / system color schemes
- Headless GraphQL adapters (`freeformHeadlessManifest` / `freeformHeadlessSubmit`)

## Coming later

- Payment fields (Stripe, etc.)
- Official Vue adapter
- Bootstrap and Tailwind theme packages
## Security checklist

1. Keep headless **disabled** until you intentionally enable forms.
2. Set explicit `headless.allowedOrigins` for cross-origin apps.
3. Enable a **captcha** on public forms (do not rely on honeypot alone for API callers).
4. Leave **`allowRawHtml` off** unless HTML / rich-text field content is trusted CMS content.
5. Do not treat client-side conditional hiding as a server access-control boundary yet.
79 changes: 76 additions & 3 deletions craft-freeform/headless/graphql.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: GraphQL
sidebar_position: 1
description: Integrate Freeform 5.x with GraphQL in Craft CMS for headless form handling and submissions.
sidebar_position: 6
description: Freeform headless GraphQL adapters plus legacy form queries and submission mutations for Craft CMS.
---

import Tabs from '@theme/Tabs';
Expand All @@ -12,13 +12,86 @@ import { VerticalStepWrapper, StepMarkdown } from '@site/src/components/docs';

# GraphQL<BadgeGroup><Badge type="lite" text="Lite" /><Badge type="pro" text="Pro" /></BadgeGroup>

Freeform supports querying form layouts and form submission mutations via GraphQL. This guide assumes you have some GraphQL experience. To learn more about GraphQL and Craft, please check out the [Fetch content with GraphQL](https://craftcms.com/docs/getting-started-tutorial/build/graphql.html) guide and [Craft's GraphQL API](https://craftcms.com/docs/5.x/development/graphql.html).
Freeform supports GraphQL in two ways:

1. **Headless adapters** (recommended when you want GraphQL) — `freeformHeadlessManifest` and `freeformHeadlessSubmit`, matching the [REST API](./rest-api.mdx) contract
2. **Legacy form queries / `save_{handle}_Submission` mutations** — still supported for existing integrations (documented below)

This guide assumes some GraphQL experience. See Craft’s [Fetch content with GraphQL](https://craftcms.com/docs/getting-started-tutorial/build/graphql.html) and [GraphQL API](https://craftcms.com/docs/5.x/development/graphql.html).

:::tip New React / Next.js projects
Prefer the official packages with the [REST API](./rest-api.mdx) for `<Freeform />` / `useFreeform()` — start with [Getting Started](./getting-started.mdx). Use the headless GraphQL adapters when your app already speaks Craft GraphQL and you want the same manifest/submit payload shape.
:::

## Headless GraphQL adapters

These adapters reuse Freeform’s headless `ManifestService` and `HeadlessSubmitService`. Enable headless for the form first (same `config/freeform.php` as REST).

### Requirements

| Requirement | Notes |
| --- | --- |
| Headless config | `headless.enabled` + per-form `exposeManifest` / `allowSubmit` |
| Craft GraphQL schema | Freeform **form read** + **submission create** for that form |
| Site access | Enable your Craft **site** on the schema (missing this returns *Schema doesn’t have access to the “Site” site*) |
| Schema token | Pass as `Authorization: Bearer <token>` |

Typical endpoint: `POST /actions/graphql/api` (or your Craft GraphQL URL).

### Manifest query

Returns the same manifest object as REST `GET /freeform/api/forms/{handle}/manifest` (the `data` payload).

```graphql showLineNumbers
query {
freeformHeadlessManifest(handle: "contact")
}
```

### Submit mutation

Returns the same structured submit response as REST (`success`, `status`, `errors`, `draft`, etc.).

```graphql showLineNumbers
mutation {
freeformHeadlessSubmit(
handle: "contact"
intent: "submit"
values: { email: "jane@example.com", message: "Hello" }
meta: {}
context: {}
)
}
```

| Argument | Description |
| --- | --- |
| `handle` | Form handle (required) |
| `intent` | `submit`, `next`, `back`, `validate`, or `saveDraft` (default `submit`) |
| `values` | Field values by handle (`FreeformJson`) |
| `meta` | Honeypot, captcha, javascriptTest, etc. |
| `context` | Draft tokens and other submit context |
| `csrfToken` | Optional; when omitted, cookie CSRF is skipped (schema token is the gate) |

### File uploads

Multipart / File Drag & Drop uploads remain on the **REST** endpoints. Use GraphQL for JSON values; upload files via REST (or the official React packages on REST).

### Try it

The [Freeform Headless React Demo](https://github.com/solspace/freeform-headless-react-demo) includes a **GraphQL** tab. Set `VITE_GRAPHQL_TOKEN` (and optional `VITE_GRAPHQL_PATH`) in `.env`.

---

<Photo
img={require('@site/static/img/craft/freeform/v5/cp/graphql.png')}
alt="GraphQL"
/>

## Legacy GraphQL (form queries and submission mutations)

The sections below document Freeform’s original GraphQL form layout queries and per-form `save_{handle}_Submission` mutations.

## Demos

Have a look at our headless demos to get a feel for what's possible with GraphQL:
Expand Down
37 changes: 29 additions & 8 deletions craft-freeform/headless/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,55 @@ import Icons from '@site/static/icons/cards';

# Headless

Freeform supports headless website architecture making it easy to use JavaScript-based front-end frameworks such as Vue.js, Next.js, React JS and more! Freeform also supports querying form layouts and using mutations to create submissions via GraphQL.
Freeform supports headless website architecture for JavaScript front ends such as React, Next.js, and Vue.js.

**Recommended for new React / Next.js projects:** the official npm packages and the headless REST API. Prefer [headless GraphQL adapters](./graphql#headless-graphql-adapters) when your app already uses Craft GraphQL.

Start with [Getting Started](./getting-started) to enable headless and install `@solspace/freeform-react`, or try the [example React demo](https://github.com/solspace/freeform-headless-react-demo).

<FlexCards
navCards
items={[
{
title: 'Vue.js',
description: 'Full support for implementation Freeform with Vue.js.',
icon: Icons.VuejsIcon,
fullCardLink: 'vuejs',
title: 'Getting Started',
description:
'Enable the headless API and install the official React packages.',
icon: Icons.HeadlessIcon,
fullCardLink: 'getting-started',
},
{
title: 'REST API',
description:
'Manifest, submit, CSRF, CORS, and file upload endpoints.',
icon: Icons.ApiGearIcon,
fullCardLink: 'rest-api',
},
{
title: 'React JS',
description: 'Full support for implementation Freeform with React JS.',
description:
'Render forms with <Freeform /> or build your own UI with useFreeform().',
icon: Icons.ReactIcon,
fullCardLink: 'reactjs',
},
{
title: 'Next.js',
description: 'Full support for implementation Freeform with Next.js.',
description:
'App Router Client Components, rewrites proxy, and CSRF-friendly setup.',
icon: Icons.NextjsIcon,
fullCardLink: 'nextjs',
filterIcon: true,
},
{
title: 'Vue.js',
description:
'Headless demos for Vue.js (official Vue package coming later).',
icon: Icons.VuejsIcon,
fullCardLink: 'vuejs',
},
{
title: 'GraphQL',
description:
'With Freeform, you can easily query form layouts and submit form mutations using GraphQL.',
'Headless manifest/submit adapters, plus legacy form queries and mutations.',
icon: Icons.GraphqlIcon,
fullCardLink: 'graphql',
},
Expand Down
Loading