Skip to main content

Next.js
LitePro
Improved in 5.16+

Use the official @solspace/freeform-react packages. Complete Getting Started (enable headless + install packages) first.

Freeform’s React packages are client-side. In the App Router, render them from a Client Component and proxy /freeform so CSRF cookies stay on your Next.js origin.

Install

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

Setup

1

Proxy Freeform through Next.js

next.config.ts
import type { NextConfig } from 'next';

const nextConfig: NextConfig = {
async rewrites() {
return [
{
source: '/freeform/:path*',
destination: `${process.env.CRAFT_URL}/freeform/:path*`,
},
];
},
};

export default nextConfig;

Set CRAFT_URL to your Craft site (for example https://cms.example.com). Requests from the browser hit /freeform/... on the Next.js origin; Next.js forwards them to Craft.

2

Create a Client Component

app/contact/contact-form.tsx
'use client';

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"
// Same-origin via the rewrite above
baseUrl=""
extensions={recommendedExtensions}
onSuccess={(response) => {
console.log(response);
}}
/>
);
}
app/contact/page.tsx
import { ContactForm } from './contact-form';

export default function ContactPage() {
return (
<main>
<h1>Contact</h1>
<ContactForm />
</main>
);
}
3

Enable the form in Freeform

In config/freeform.php on Craft:

'headless' => [
'enabled' => true,
'forms' => [
'contact' => [
'exposeManifest' => true,
'allowSubmit' => true,
],
],
],

With the same-origin proxy you usually do not need to add the Next.js origin to allowedOrigins. Add it only if the browser calls Craft directly.

Environment Variables

VariableWhereExample
CRAFT_URLNext.js server (rewrites)https://cms.example.com
NEXT_PUBLIC_CRAFT_URLOptional public Craft URLOnly if you skip the proxy

Prefer the proxy + baseUrl="" pattern over exposing Craft’s origin to the browser.

Cross-origin (no proxy)

If the browser must call Craft directly:

  1. Set baseUrl={process.env.NEXT_PUBLIC_CRAFT_URL}
  2. Add your Next.js origin to headless.allowedOrigins
  3. Keep credentials enabled (default in the Freeform client)
<Freeform
handle="contact"
baseUrl={process.env.NEXT_PUBLIC_CRAFT_URL}
extensions={recommendedExtensions}
/>

Headless Markup

useFreeform() works the same as in a Vite React app — still inside a Client Component:

'use client';

import { useFreeform } from '@solspace/freeform-react';

export function HeadlessContactForm() {
const form = useFreeform({
handle: 'contact',
baseUrl: '',
});

// …build your own fields with form.getFieldProps(), etc.
}

See React JS for themes (including the official Tailwind and Bootstrap starters), captchas, Stripe, Square, PayPal, and Mollie payments, custom renderers, and the full hook API.

File Uploads

Official packages handle multipart submits and File Drag & Drop via @solspace/freeform-extensions. You do not need the older GraphQL Base64 upload guides for new projects.

Payments

Stripe Payment Element forms work with the same Client Component + /freeform rewrite pattern. Pass recommendedExtensions and follow React JS → Stripe payments. Keep Stripe webhooks pointed at your Craft site.

Legacy Demos

Older Next.js GraphQL / AJAX demos:

Recommended path for new work: this page + Getting Started.