npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wordpuppi/anyform-next

v0.5.1

Published

Next.js integration for anyform - RSC support and Server Actions

Readme

@wordpuppi/anyform-next

Next.js integration for anyform — React Server Components and Server Actions support.

Installation

npm install @wordpuppi/anyform-next

Quick Start (App Router)

// app/contact/page.tsx
import { AnyFormRSC } from '@wordpuppi/anyform-next';

export default function ContactPage() {
  return (
    <AnyFormRSC slug="contact" options={{ tailwind: true }}>
      {(form) => (
        <form {...form.getFormProps()}>
          {form.visibleFields.map((field) => (
            <div key={field.name}>
              <label {...form.getLabelProps(field.name)} />
              <input {...form.getFieldProps(field.name)} />
              {form.errors[field.name]?.map((err, i) => (
                <span key={i} className="text-red-500">{err}</span>
              ))}
            </div>
          ))}
          <button type="submit" disabled={form.isSubmitting}>
            {form.isSubmitting ? 'Submitting...' : 'Submit'}
          </button>
        </form>
      )}
    </AnyFormRSC>
  );
}

Features

  • React Server Components — Form schema fetched on the server
  • Server Actions — Optional server-side form submission
  • ISR Support — Schema cached with revalidation
  • Re-exports @wordpuppi/anyform-react — All hooks and types included

API Reference

<AnyFormRSC> Component

React Server Component that fetches form schema on the server and hydrates on the client.

import { AnyFormRSC } from '@wordpuppi/anyform-next';

<AnyFormRSC
  slug="contact"                    // Form slug (required)
  baseUrl="http://localhost:3000"   // API base URL (optional, uses ANYFORM_API_URL env)
  options={{ tailwind: true }}      // Options passed to useAnyForm
>
  {(form) => (
    <form {...form.getFormProps()}>
      {/* Your form UI */}
    </form>
  )}
</AnyFormRSC>

Server Functions

Import from @wordpuppi/anyform-next/server:

import { fetchFormSchema, submitForm } from '@wordpuppi/anyform-next/server';

fetchFormSchema(slug, baseUrl?)

Cached server-side form schema fetcher. Uses React's cache() for deduplication.

// app/forms/[slug]/page.tsx
import { fetchFormSchema } from '@wordpuppi/anyform-next/server';
import { ClientForm } from './client-form';

export default async function FormPage({ params }: { params: { slug: string } }) {
  const schema = await fetchFormSchema(params.slug);

  return <ClientForm initialSchema={schema} />;
}

submitForm(slug, data, options?)

Server Action for form submission.

// app/contact/actions.ts
'use server';

import { submitForm } from '@wordpuppi/anyform-next/server';

export async function handleSubmit(values: Record<string, unknown>) {
  const result = await submitForm('contact', values);

  if (result.success) {
    // Handle success
    return { success: true, data: result.data };
  } else {
    // Handle error
    return { success: false, error: result.error };
  }
}
// app/contact/form.tsx
'use client';

import { useAnyForm } from '@wordpuppi/anyform-next';
import { handleSubmit } from './actions';

function ContactForm({ initialSchema }) {
  const form = useAnyForm('contact', {
    initialSchema,
    onSubmit: handleSubmit,
  });

  // ...
}

Environment Variables

| Variable | Description | |----------|-------------| | ANYFORM_API_URL | Default API base URL for server-side fetching |

# .env.local
ANYFORM_API_URL=http://localhost:3000

Usage Patterns

Pattern 1: Full RSC (Recommended)

Best for SEO and initial load performance.

// app/contact/page.tsx
import { AnyFormRSC } from '@wordpuppi/anyform-next';

export default function ContactPage() {
  return (
    <AnyFormRSC slug="contact" options={{ tailwind: true }}>
      {(form) => <YourFormUI form={form} />}
    </AnyFormRSC>
  );
}

Pattern 2: Manual Schema Fetch

For more control over the data fetching.

// app/contact/page.tsx
import { fetchFormSchema } from '@wordpuppi/anyform-next/server';
import { ContactForm } from './form';

export default async function ContactPage() {
  const schema = await fetchFormSchema('contact');

  return <ContactForm schema={schema} />;
}

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

import { useAnyForm } from '@wordpuppi/anyform-next';

export function ContactForm({ schema }) {
  const form = useAnyForm('contact', {
    initialSchema: schema,
    tailwind: true,
  });

  return <form {...form.getFormProps()}>...</form>;
}

Pattern 3: Client-Only

For dynamic forms or when RSC isn't needed.

'use client';

import { useAnyForm } from '@wordpuppi/anyform-next';

export function ContactForm() {
  const form = useAnyForm('contact', { tailwind: true });

  if (form.isLoading) return <div>Loading...</div>;

  return <form {...form.getFormProps()}>...</form>;
}

Pattern 4: With Server Actions

For server-side form submission.

// app/contact/page.tsx
import { AnyFormRSC } from '@wordpuppi/anyform-next';
import { submitForm } from '@wordpuppi/anyform-next/server';

async function handleSubmit(values: Record<string, unknown>) {
  'use server';
  return submitForm('contact', values);
}

export default function ContactPage() {
  return (
    <AnyFormRSC
      slug="contact"
      options={{
        tailwind: true,
        onSubmit: handleSubmit,
      }}
    >
      {(form) => <YourFormUI form={form} />}
    </AnyFormRSC>
  );
}

Multi-Step Forms

Works the same as @wordpuppi/anyform-react:

<AnyFormRSC slug="onboarding">
  {(form) => (
    <form {...form.getFormProps()}>
      {form.step && (
        <div>Step {form.step.progress[0]} of {form.step.progress[1]}</div>
      )}

      {form.visibleFields.map((field) => (
        <input key={field.name} {...form.getFieldProps(field.name)} />
      ))}

      <div>
        {form.step?.canGoPrev && (
          <button type="button" onClick={form.prevStep}>Back</button>
        )}
        {form.step?.isLast ? (
          <button type="submit">Submit</button>
        ) : (
          <button type="button" onClick={form.nextStep}>Next</button>
        )}
      </div>
    </form>
  )}
</AnyFormRSC>

Re-exports

This package re-exports everything from @wordpuppi/anyform-react:

import {
  // Hooks
  useAnyForm,

  // Components
  AnyForm,
  AnyFormProvider,

  // Next.js specific
  AnyFormRSC,
  AnyFormClient,

  // Types
  type FormJson,
  type FieldJson,
  type UseAnyFormReturn,
  // ... etc
} from '@wordpuppi/anyform-next';

TypeScript

Full type definitions included:

import type {
  AnyFormRSCProps,
  AnyFormClientProps,
} from '@wordpuppi/anyform-next';

import type {
  SubmitFormResult,
} from '@wordpuppi/anyform-next/server';

License

MIT