@logic-joe/logicforms-react
v0.0.35
Published
React component wrapper for LogicForms - lightweight form embedding
Maintainers
Readme
@logic-joe/logicforms-react
React components for embedding LogicForms. Features Zod validation, React Hook Form integration, file uploads, localization, and full customization support.
Installation
npm install @logic-joe/logicforms-react
# or
pnpm add @logic-joe/logicforms-react
# or
yarn add @logic-joe/logicforms-reactPeer Dependencies: React 18+ or 19+
Quick Start
import { Form } from "@logic-joe/logicforms-react";
function App() {
return (
<Form
formId="your-form-id"
baseUrl="https://logicforms.onrender.com"
onSubmit={(data) => console.log("Submitted:", data)}
onError={(error) => console.error("Error:", error)}
/>
);
}Form Component Props
interface FormProps {
// Required
formId: string;
// Configuration
baseUrl?: string; // Default: "https://logicforms.onrender.com"
className?: string; // Additional CSS class
theme?: "light" | "dark" | "system"; // Default: "light"
// Localization
locale?: string; // Explicit locale (e.g., "de", "fr")
autoDetectLocale?: boolean; // Auto-detect from browser (default: true)
onLocaleChange?: (locale: string, availableLocales: string[]) => void;
// Callbacks
onSubmit?: (data: Record<string, string>) => void;
onError?: (error: Error) => void;
// Cookie consent
cookiesAccepted?: boolean; // When false, blocks third-party scripts and shows overlay
onRequestCookieConsent?: () => void; // Called when user clicks consent button
// Preview mode (for editor integration)
previewMode?: boolean;
previewData?: PreviewModeData;
forceValidation?: boolean;
hidePreviewBanner?: boolean;
// Custom renderers (see Custom Rendering section)
renderInput?: RenderInputFn;
renderTextarea?: RenderTextareaFn;
renderSelect?: RenderSelectFn;
// ... and more
}Custom UI with useMaxForm Hook
For full control over form rendering, use the useMaxForm hook:
import { useMaxForm } from "@logic-joe/logicforms-react";
function CustomForm() {
const {
form, // React Hook Form instance
fields, // Form field definitions
layout, // Layout blocks
settings, // Form settings
isLoading,
isSubmitting,
isSuccess,
error,
submitForm,
resetForm,
getFieldError,
getFieldById,
} = useMaxForm({
formId: "your-form-id",
baseUrl: "https://logicforms.onrender.com",
mode: "onBlur", // Validation mode
});
if (isLoading) return <div>Loading...</div>;
if (isSuccess) return <div>{settings.successMessage}</div>;
return (
<form onSubmit={form.handleSubmit(submitForm)}>
{fields.map((field) => (
<div key={field.id}>
<label>{field.label}</label>
<input {...form.register(field.name)} />
{getFieldError(field.name) && (
<span className="error">{getFieldError(field.name)}</span>
)}
</div>
))}
<button type="submit" disabled={isSubmitting}>
{settings.submitButtonText}
</button>
</form>
);
}File Upload
The FileUpload component handles the complete upload flow including virus scanning:
import { FileUpload } from "@logic-joe/logicforms-react";
function FileField({ field, formId }) {
const [fileId, setFileId] = useState("");
return (
<FileUpload
field={field}
formId={formId}
baseUrl="https://logicforms.onrender.com"
value={fileId}
onChange={setFileId}
/>
);
}The component automatically:
- Gets a presigned upload URL
- Uploads to the quarantine bucket
- Triggers antivirus scanning
- Polls for scan completion
- Shows upload progress and status
Localization
Forms support multiple languages with automatic browser detection:
// Auto-detect browser locale (default behavior)
<Form formId="abc" autoDetectLocale={true} />
// Explicit locale
<Form formId="abc" locale="de" />
// React to locale changes
<Form
formId="abc"
onLocaleChange={(locale, availableLocales) => {
console.log("Current:", locale);
console.log("Available:", availableLocales);
}}
/>useFormLocale Hook
For building custom locale selectors:
import { useFormLocale } from "@logic-joe/logicforms-react";
function LocaleSelector() {
const { locale, setLocale, availableLocales, isLoading } = useFormLocale({
formId: "your-form-id",
baseUrl: "https://logicforms.onrender.com",
});
return (
<select value={locale} onChange={(e) => setLocale(e.target.value)}>
{availableLocales.map((loc) => (
<option key={loc} value={loc}>
{loc}
</option>
))}
</select>
);
}CAPTCHA Integration
Google reCAPTCHA
Configure reCAPTCHA in your form settings (dashboard). The component handles v2 and v3 automatically:
// reCAPTCHA is automatically rendered when enabled in form settings
<Form formId="abc" />Cloudflare Turnstile
Similarly configured via form settings:
// Turnstile widget rendered automatically when enabled
<Form formId="abc" />Theming
Theme Mode
// Light theme (default)
<Form formId="abc" theme="light" />
// Dark theme
<Form formId="abc" theme="dark" />
// Follow system preference
<Form formId="abc" theme="system" />Custom Theme Colors
Theme colors can be configured in the form settings (dashboard). The component automatically applies them:
interface ThemeColors {
primary?: string;
primaryForeground?: string;
background?: string;
foreground?: string;
muted?: string;
mutedForeground?: string;
border?: string;
input?: string;
destructive?: string;
}Custom CSS
Forms support custom CSS defined in the dashboard. The CSS is automatically scoped to the form:
/* Example custom CSS (defined in dashboard) */
.logicforms-{formId} input {
border-radius: 8px;
}Custom Rendering
Override any part of the form with custom render functions:
Field Renderers
<Form
formId="abc"
renderInput={(ctx) => (
<input
{...ctx.register(ctx.name)}
placeholder={ctx.field.placeholder}
className={ctx.error ? "error" : ""}
/>
)}
renderTextarea={(ctx) => <textarea {...ctx.register(ctx.name)} rows={6} />}
renderSelect={(ctx) => (
<select value={ctx.value} onChange={(e) => ctx.onChange(e.target.value)}>
{ctx.field.options.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
)}
/>Field Render Context
interface FieldRenderContext {
field: FormField; // Field definition
register: UseFormRegister; // React Hook Form register
error?: FieldError; // Validation error
value: string; // Current value
onChange: (value: string) => void;
onBlur: () => void;
name: string;
disabled?: boolean;
}Layout Block Renderers
<Form
formId="abc"
renderHeading={(block) => <h2 style={{ color: "blue" }}>{block.content}</h2>}
renderTextBlock={(block) => <p className="custom-text">{block.content}</p>}
renderDivider={() => <hr className="my-divider" />}
renderRow={(block, children) => <div className="custom-row">{children}</div>}
/>State Renderers
<Form
formId="abc"
renderLoading={() => <Spinner />}
renderSuccess={(message) => <div className="success-banner">{message}</div>}
renderFormError={(error) => <div className="error-alert">{error}</div>}
renderFieldError={(error) => (
<span className="field-error">{error.message}</span>
)}
renderSubmit={({ isSubmitting, buttonText, hasErrors }) => (
<button type="submit" disabled={isSubmitting || hasErrors}>
{isSubmitting ? "Sending..." : buttonText}
</button>
)}
renderFieldWrapper={(field, children, error) => (
<div className={`field-group ${error ? "has-error" : ""}`}>{children}</div>
)}
/>Cookie Consent
If your form uses third-party services (GA4, reCAPTCHA, Turnstile), you need user consent before loading their scripts (GDPR/ePrivacy). Use cookiesAccepted to control this:
import { useState } from "react";
import { Form } from "@logic-joe/logicforms-react";
function MyForm() {
const [cookiesAccepted, setCookiesAccepted] = useState(false);
return (
<Form
formId="your-form-id"
cookiesAccepted={cookiesAccepted}
onRequestCookieConsent={() => {
// Open your cookie banner/dialog
myCookieBanner.show();
}}
/>
);
// Later, when user accepts: setCookiesAccepted(true)
}When cookiesAccepted is false, the form is blurred and a localized consent overlay is shown. First-party analytics (self-hosted, no cookies) continue to work. Omitting cookiesAccepted preserves backwards compatibility — all scripts load normally.
Analytics (Google Analytics 4)
When enabled in form settings, the component automatically tracks:
form_view- Form loadedform_start- First field interactionform_submit- Successful submissionform_error- Submission error
useAnalytics Hook
For custom analytics implementations:
import { useAnalytics } from "@logic-joe/logicforms-react";
const { trackFormView, trackFormStart, trackFormSubmit, trackFormError } =
useAnalytics({
measurementId: "G-XXXXXXXXXX",
formId: "your-form-id",
formName: "Contact Form",
});Validation Utilities
Build custom validation schemas:
import {
buildFieldSchema,
buildFormSchema,
validateFormData,
} from "@logic-joe/logicforms-react";
import type { FormField } from "@logic-joe/logicforms-api";
// Build schema for a single field
const fieldSchema = buildFieldSchema(field);
// Build schema for entire form
const formSchema = buildFormSchema(fields);
// Validate data
const result = validateFormData(formSchema, data);Block Components
For custom layouts, use the exported block components:
import {
HeadingBlock,
TextBlock,
DividerBlock,
RowBlock,
} from "@logic-joe/logicforms-react";
<HeadingBlock block={{ content: "Title", level: 2 }} />
<TextBlock block={{ content: "Description text" }} />
<DividerBlock />
<RowBlock block={rowBlock} renderChild={renderBlock} />TypeScript Support
Full TypeScript support with exported types:
import type {
FormProps,
FormData,
FormSettingsData,
ThemeMode,
FieldRenderContext,
FileFieldRenderContext,
RenderInputFn,
RenderTextareaFn,
RenderSelectFn,
RenderCheckboxFn,
RenderRadioFn,
RenderFileFn,
RenderHeadingFn,
RenderSubmitFn,
SubmitRenderProps,
UseMaxFormOptions,
UseMaxFormReturn,
UseFormLocaleOptions,
UseFormLocaleReturn,
UseAnalyticsOptions,
} from "@logic-joe/logicforms-react";UMD Build
For non-bundler usage, a UMD build is available:
<script src="https://unpkg.com/@logic-joe/logicforms-react"></script>
<script>
const { Form, useMaxForm, render } = window.LogicForms;
// Or use the render helper
LogicForms.render("#form-container", {
formId: "your-form-id",
baseUrl: "https://logicforms.onrender.com",
});
</script>License
MIT
