Dynamic Forms

New Feature

Use forms that automatically sync with the admin interface - no more copy-pasting code!

What are Dynamic Forms?
Dynamic Forms automatically fetch their configuration from the database on the server and render themselves based on your admin settings. Server-side logging happens automatically.

❌ Before (Old Way)

  • ❌ Two files: page.tsx + client.tsx
  • ❌ Copy generated code from admin
  • ❌ Paste into client.tsx
  • ❌ Re-copy when form changes
  • ❌ Risk of code getting out of sync
  • ❌ Manual server-side logging setup

✅ Now (Dynamic Forms)

  • ✅ One file: just page.tsx
  • ✅ One line of code: <DynamicForm slug="form-name" />
  • ✅ Automatically syncs with admin changes
  • ✅ Server-side form loading & logging
  • ✅ Always up-to-date
  • ✅ No maintenance required
Basic Usage
The simplest way to use a dynamic form
import DynamicForm from '@/components/DynamicForm'

export default function ContactPage() {
  return (
    <div className="max-w-md mx-auto">
      <h1>Contact Us</h1>
      <DynamicForm slug="contact-us" />
    </div>
  )
}
Advanced Usage
Customize styling, handle events, and add reCAPTCHA
import DynamicForm from '@/components/DynamicForm'
import { useRouter } from 'next/navigation'

export default function NewsletterPage() {
  const router = useRouter()

  return (
    <div className="max-w-lg mx-auto">
      <DynamicForm 
        slug="newsletter-signup"
        theme="shadcn"
        className="space-y-6"
        containerClassName="bg-white p-6 rounded-lg shadow-md"
        recaptcha={true}
        onSuccess={(result) => {
          console.log('Newsletter signup successful!', result)
          
          // Handle different confirmation types
          if (result.confirmation?.type === 'redirect') {
            router.push(result.confirmation.redirectUrl!)
          } else {
            // Show success message (handled automatically)
          }
        }}
        onError={(error) => {
          console.error('Newsletter signup failed:', error)
          // Custom error handling
        }}
        onSubmit={(data) => {
          console.log('Form data being submitted:', data)
          // Access form data before submission
        }}
      />
    </div>
  )
}
Available Themes
Choose the styling approach that works best for your project

shadcn

Uses shadcn/ui components (recommended)

  • ✅ Consistent with your UI
  • ✅ Accessible components
  • ✅ Professional styling

html

Plain HTML with Tailwind classes

  • ✅ No dependencies
  • ✅ Lightweight
  • ✅ Easy to customize

custom

Minimal markup for custom styling

  • ✅ Full control
  • ✅ CSS classes for targeting
  • ✅ Custom renderers
Custom Field Rendering
Take complete control over how fields are rendered
import DynamicForm from '@/components/DynamicForm'
import { FormField } from '@/feature/form/formTypes'

function CustomFieldRenderer(field: FormField, index: number) {
  if (field.type === 'email') {
    return (
      <div key={index} className="my-custom-email-field">
        <label className="fancy-label">{field.label}</label>
        <input 
          type="email" 
          name={field.name}
          required={field.required}
          className="fancy-input"
          placeholder={`Enter your ${field.label.toLowerCase()}`}
        />
      </div>
    )
  }
  
  // Return null to use default renderer for other field types
  return null
}

export default function CustomFormPage() {
  return (
    <DynamicForm 
      slug="custom-styled-form"
      theme="custom"
      customFieldRenderer={CustomFieldRenderer}
    />
  )
}
Props Reference
Complete list of available props and their types
PropTypeDefaultDescription
slugstring-Required. The form slug from admin
theme'shadcn' | 'html' | 'custom''shadcn'Styling theme to use
classNamestring''CSS classes for the form element
containerClassNamestring''CSS classes for the container div
recaptchabooleanfalseEnable reCAPTCHA integration
onSuccessfunction-Called when form submits successfully
onErrorfunction-Called when form submission fails
onSubmitfunction-Called before submission with form data
customFieldRendererfunction-Custom function to render specific fields
Live Example
See a dynamic form in action (requires a form with slug "contact-us" to exist)
Loading form...

This will show an error if you don't have a form with slug "contact-us" - that's normal for this demo.

Migrating from Generated Code
How to switch from the old 2-file pattern to the new 1-file pattern

❌ Before (Old Pattern)

src/app/forms/contact/page.tsx

import createLog from '@/feature/logs/createLog'
import ContactForm from './client'
import db from '@/feature/db/db'

export default async function ContactPage() {
  await createLog({
    db,
    eventType: 'form.viewed',
    metadata: {
      formId: '400ee4e2-761e-41de-913b-9a17f636e0c3',
      slug: 'contact',
    },
  })
  return <ContactForm />
}

src/app/forms/contact/client.tsx

'use client'
import { useState } from 'react'
import { Input } from '@/components/ui/input'
// ... lots of imports and form JSX code
// ... 94 lines of generated code ...

✅ After (Dynamic Pattern)

src/app/forms/contact/page.tsx

import DynamicForm from '@/components/DynamicForm'

export default async function ContactPage() {
  return (
    <div className="max-w-md mx-auto">
      <h1>Contact Us</h1>
      <DynamicForm slug="contact" />
    </div>
  )
}

That's it! Server-side logging happens automatically inside DynamicForm.

Migration Steps

Step 1: Identify Your Forms

Find form directories with the old pattern: page.tsx (with createLog call) + client.tsx (with form JSX and requestSubmitForm).

Step 2: Note the Form Slug

In your client.tsx, find the formSlug value passed to requestSubmitForm.

Step 3: Replace Both Files

Delete client.tsx and replace your page.tsx content with just <DynamicForm slug="your-slug" />. Server-side logging happens automatically.

Step 4: Add Event Handlers (Optional)

If you had custom success/error handling, move it to the onSuccess and onError props.

Step 5: Test & Deploy

Test that your form works the same way. Now you can edit the form in admin and it will automatically update!

Elder & Estate

Protecting your legacy, one plan at a time.

Elder and Estate is an online service providing legal forms and information. Elder and Estate is not a law firm and cannot provide legal advice. Elder and Estate is not a substitute for an attorney or law firm. Communications between you and Elder and Estate are protected by our Privacy Policy, not by attorney-client privilege. 2025 Elder and Estate, Inc.

Estate Planning - Create an Online Will and Trust | Elder and Estate