@mackehansson/schemaform

Framework Notes

Use Schemaform in Next.js, React Router, and other React hosts.

Schemaform's runtime API is the same across React hosts. The canonical examples use a plain Vite + React baseline so the package behavior is clear without route-framework assumptions.

Next.js

The first-party ShadCN Renderer and the headless React hooks are client-side React code. In the Next.js App Router, place components that render forms or call Schemaform hooks behind a 'use client' boundary.

"use client";

import { SchemaForm } from "@mackehansson/schemaform-shadcn";
import type { FormDefinition, WorkingState } from "@mackehansson/schemaform";

export function ContactFormClient({
  definition,
}: {
  definition: FormDefinition;
}) {
  return (
    <SchemaForm
      definition={definition}
      onSubmit={(payload: WorkingState) => {
        console.log(payload);
      }}
    />
  );
}

Server Components can load the persisted Form Definition and pass it into a client component as serializable JSON.

import { migrate } from "@mackehansson/schemaform";
import { ContactFormClient } from "./contact-form-client";

export default async function ContactPage() {
  const rawDefinition = await loadDefinitionFromDatabase("contact");
  const definition = migrate(rawDefinition);

  return <ContactFormClient definition={definition} />;
}

Keep non-serializable values on the client side. Functions such as onSubmit, translate, widget registry entries, and custom validators cannot cross the React Server Components boundary as props from a Server Component.

If your Next app also uses server actions, call them from the client component's submit handler or wrap them in an app-specific API route. Schemaform does not require a special transport.

React Router

React Router needs nothing special. Render SchemaForm, useFormRenderer, or useFormBuilder inside route components the same way you would in Vite.

import { SchemaForm } from "@mackehansson/schemaform-shadcn";
import type { FormDefinition } from "@mackehansson/schemaform";

export function ContactRoute({ definition }: { definition: FormDefinition }) {
  return (
    <SchemaForm
      definition={definition}
      onSubmit={(payload) => console.log(payload)}
    />
  );
}

Use your router's loader, action, or data-fetching pattern to read and save the versioned Form Definition envelope. The Schemaform API does not change.

Other React hosts

The same rule applies in any router, desktop shell, or embedded widget: load a Form Definition, migrate it if it came from storage, and render it with the ShadCN Renderer or your own Renderer built on useFormRenderer.

Host frameworks decide routing, loading, submission transport, and persistence. Schemaform owns the Core model, the React hooks, and the Renderer contract.

On this page