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.