Next.jsLiteProImproved 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
Proxy Freeform through Next.js
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.
Create a Client Component
'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);
}}
/>
);
}
import { ContactForm } from './contact-form';
export default function ContactPage() {
return (
<main>
<h1>Contact</h1>
<ContactForm />
</main>
);
}
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
| Variable | Where | Example |
|---|---|---|
CRAFT_URL | Next.js server (rewrites) | https://cms.example.com |
NEXT_PUBLIC_CRAFT_URL | Optional public Craft URL | Only 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:
- Set
baseUrl={process.env.NEXT_PUBLIC_CRAFT_URL} - Add your Next.js origin to
headless.allowedOrigins - 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
- Square
- PayPal
- Mollie
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.
Square Web Payments SDK forms work with the same Client Component + /freeform rewrite pattern. Pass recommendedExtensions and follow React JS → Square payments.
PayPal Buttons forms work with the same Client Component + /freeform rewrite pattern. Pass recommendedExtensions and follow React JS → PayPal payments.
Mollie hosted-checkout forms work with the same Client Component + /freeform rewrite pattern. Pass recommendedExtensions and follow React JS → Mollie payments. Mollie callback/webhook must reach your Craft site.
Legacy Demos
Older Next.js GraphQL / AJAX demos:
Recommended path for new work: this page + Getting Started.