Getting Started
Next.js

Next.js

better-form works seamlessly with Next.js, supporting both the App Router and Pages Router.

better-form is a client-side library. In Next.js App Router, you'll need to use the "use client" directive.

Installation

npm install @better_form/core

Setup

App Router

1. Import styles in your root layout:

app/layout.tsx
import '@better_form/core/styles';
import type { Metadata } from 'next';
 
export const metadata: Metadata = {
  title: 'My App',
};
 
export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  );
}

2. Create a client component for your form:

app/components/MyForm.tsx
'use client';
 
import { WizardProvider, WizardContainer, defaultFieldComponents } from '@better_form/core';
import type { WizardConfig } from '@better_form/core';
 
const config: WizardConfig = {
  id: 'contact-form',
  steps: [
    {
      id: 'contact',
      title: 'Contact Information',
      fieldGroups: [
        {
          id: 'personal',
          fields: [
            {
              id: 'name',
              name: 'name',
              label: 'Full Name',
              type: 'text',
              validation: { required: true },
            },
            {
              id: 'email',
              name: 'email',
              label: 'Email Address',
              type: 'email',
              validation: { required: true },
            },
          ],
        },
      ],
    },
  ],
};
 
export function MyForm() {
  const handleSubmit = async (data: Record<string, unknown>) => {
    // Handle form submission
    console.log('Form submitted:', data);
  };
 
  return (
    <WizardProvider
      config={config}
      fieldComponents={defaultFieldComponents}
      onSubmit={handleSubmit}
    >
      <WizardContainer />
    </WizardProvider>
  );
}

3. Use the component in your page:

app/contact/page.tsx
import { MyForm } from '../components/MyForm';
 
export default function ContactPage() {
  return (
    <main>
      <h1>Contact Us</h1>
      <MyForm />
    </main>
  );
}

Server Actions Integration

You can combine better-form with Next.js Server Actions for form handling:

app/actions.ts
'use server';
 
export async function submitForm(data: Record<string, unknown>) {
  // Server-side form processing
  // e.g., save to database, send email, etc.
 
  return { success: true };
}
app/components/MyForm.tsx
'use client';
 
import { WizardProvider, WizardContainer, defaultFieldComponents } from '@better_form/core';
import type { WizardConfig } from '@better_form/core';
import { submitForm } from '../actions';
 
// ... config ...
 
export function MyForm() {
  const handleSubmit = async (data: Record<string, unknown>) => {
    const result = await submitForm(data);
    if (result.success) {
      // Redirect or show success message
    }
  };
 
  return (
    <WizardProvider
      config={config}
      fieldComponents={defaultFieldComponents}
      onSubmit={handleSubmit}
    >
      <WizardContainer />
    </WizardProvider>
  );
}

With Plugins

If using plugins like Google Places, import their styles too:

app/layout.tsx
import '@better_form/core/styles';
import '@better_form/plugin-google-places/styles';

And configure the plugin in your form:

app/components/AddressForm.tsx
'use client';
 
import { WizardProvider, WizardContainer, defaultFieldComponents } from '@better_form/core';
import { googlePlacesPlugin } from '@better_form/plugin-google-places';
 
// Enable the plugin
const fieldComponents = {
  ...defaultFieldComponents,
  ...googlePlacesPlugin.components,
};

Troubleshooting

Hydration Errors

If you encounter hydration errors, ensure your form component is marked with "use client" directive.

Styles Not Loading

Make sure you're importing styles in the correct file:

  • App Router: app/layout.tsx
  • Pages Router: pages/_app.tsx

TypeScript Errors

Ensure your tsconfig.json includes the necessary compiler options:

tsconfig.json
{
  "compilerOptions": {
    "moduleResolution": "bundler",
    "esModuleInterop": true
  }
}