Quick Start
The simplest way to add a form to your page:
import { Mantlz } from '@mantlz/nextjs';
export default function ContactPage() {
return (
<div className="container mx-auto p-4">
<Mantlz formId="your-form-id" />
</div>
);
}
That’s it! The form will automatically:
- Fetch its schema from the Mantlz API
- Render with appropriate fields
- Handle validation and submission
- Show success/error notifications
Using the Hook
Access the Mantlz client directly using the useMantlz hook:
app/components/CustomForm.tsx
'use client';
import { useMantlz } from '@mantlz/nextjs';
import { useEffect, useState } from 'react';
export default function CustomForm() {
const { client, apiKey } = useMantlz();
const [userCount, setUserCount] = useState(0);
useEffect(() => {
async function fetchCount() {
if (!client) return;
const count = await client.getUsersJoinedCount('your-form-id');
setUserCount(count);
}
fetchCount();
}, [client]);
return (
<div>
<p>{userCount} users have joined!</p>
<Mantlz formId="your-form-id" />
</div>
);
}
Component Props
The Mantlz component accepts the following props:
The unique identifier for your form from the Mantlz dashboard
theme
'default' | 'modern' | 'neobrutalism' | 'simple'
Built-in theme to apply to the form. Default: 'default'
Custom appearance configuration for deep styling (see Customization)
Display the number of users who have joined (for waitlist forms). Default: false
Override the API-fetched user count with a custom number
Custom label for the users joined display. Default: 'people have joined'
URL to redirect to after successful form submission (STANDARD/PRO plans only)
Additional CSS classes to apply to the form container
When a user submits a form, the following happens:
- Client-side validation - Form data is validated using Zod schemas
- API request - Data is sent to the Mantlz API via the SDK client
- Server processing - The Mantlz backend processes and stores the submission
- Response handling - The SDK handles success/error states
- Redirect - User is redirected to the configured URL or default thank-you page
// The SDK handles this entire flow automatically
<Mantlz
formId="contact-form"
redirectUrl="/thank-you"
/>
Redirect Behavior
Redirect behavior depends on your Mantlz plan:
FREE Plan
STANDARD/PRO Plans
Users are always redirected to the Mantlz hosted thank-you page, even if you provide a custom redirectUrl.<Mantlz
formId="form-id"
redirectUrl="/custom-thanks" // Ignored on FREE plan
/>
A toast notification will inform users that custom redirects require an upgrade. Users are redirected to your custom URL after successful submission.<Mantlz
formId="form-id"
redirectUrl="/thank-you" // Respected on paid plans
/>
Supports both relative and absolute URLs:
- Relative:
/thank-you, /success?ref=form
- Absolute:
https://yoursite.com/thanks
Error Handling
The SDK includes comprehensive error handling:
Automatic Error Display
// Errors are automatically shown as toast notifications
<Mantlz formId="form-id" />
Error types handled:
- 401 Unauthorized - Invalid or inactive API key
- 404 Not Found - Form ID doesn’t exist
- 409 Conflict - Duplicate submission (e.g., email already registered)
- 400 Bad Request - Invalid form data
- 500 Server Error - Internal server error
Disabling Notifications
To handle errors manually, disable notifications:
import { createMantlzClient } from '@mantlz/nextjs';
export const mantlzClient = createMantlzClient(
process.env.MANTLZ_KEY,
{
notifications: false, // Disable automatic toasts
}
);
Then handle errors manually:
'use client';
import { useMantlz } from '@mantlz/nextjs';
import { useState } from 'react';
export default function ManualErrorHandling() {
const { client } = useMantlz();
const [error, setError] = useState<string | null>(null);
async function handleSubmit(data: any) {
try {
await client?.submitForm('contact', {
formId: 'form-id',
data,
});
} catch (err: any) {
setError(err.userMessage || err.message);
}
}
return (
<div>
{error && <div className="error">{error}</div>}
{/* Your custom form UI */}
</div>
);
}
Loading States
The Mantlz component includes built-in loading states:
// Shows a spinner while fetching form schema
<Mantlz formId="form-id" />
Loading states:
- Initial mount - Component is mounting (very brief)
- Schema fetch - Loading form configuration from API
- Submission - Form is being submitted (button shows spinner)
For advanced use cases, submit forms programmatically:
'use client';
import { useMantlz } from '@mantlz/nextjs';
export default function ProgrammaticSubmission() {
const { client } = useMantlz();
async function submitForm() {
if (!client) return;
const result = await client.submitForm('waitlist', {
formId: 'waitlist-form',
data: {
email: '[email protected]',
name: 'John Doe',
},
redirectUrl: '/welcome',
});
if (result.success) {
console.log('Submission ID:', result.submissionId);
}
}
return (
<button onClick={submitForm}>
Join Waitlist
</button>
);
}
The submitForm method accepts:
Form type: 'waitlist', 'contact', 'feedback', 'survey', etc.
The form ID from your dashboard
Form data object matching your form schema
Custom redirect URL (STANDARD/PRO plans only)
Override API key for this specific request
reCAPTCHA token for spam protection
Returns:
interface FormSubmitResponse {
success: boolean;
submissionId?: string;
message?: string;
error?: MantlzError;
isConflict?: boolean;
redirect?: {
url: string;
allowed: boolean;
reason?: string;
};
}
Access form configuration programmatically:
'use client';
import { useMantlz } from '@mantlz/nextjs';
import { useEffect, useState } from 'react';
import type { FormSchema } from '@mantlz/nextjs';
export default function FormInfo() {
const { client } = useMantlz();
const [schema, setSchema] = useState<FormSchema | null>(null);
useEffect(() => {
async function fetchSchema() {
if (!client) return;
const formSchema = await client.getFormSchema('form-id');
setSchema(formSchema);
}
fetchSchema();
}, [client]);
if (!schema) return <div>Loading...</div>;
return (
<div>
<h2>{schema.name}</h2>
<p>{schema.description}</p>
<ul>
{schema.fields.map(field => (
<li key={field.id}>
{field.label} ({field.type})
{field.required && ' *'}
</li>
))}
</ul>
</div>
);
}
Best Practices
Always use the useMantlz hook in client components - The hook requires React Context, which only works in client components ('use client').
Let the SDK handle redirects - Don’t manually redirect after form submission. The SDK handles this intelligently based on plan tier.
Don’t disable notifications without implementing custom error handling - Users need feedback when submissions fail.
Form schemas are cached - The SDK caches form schemas for 5 minutes by default to reduce API calls.
Next Steps
Form Types
Explore all available form types and their features
Theming
Customize form appearance with themes
Customization
Deep dive into advanced customization options