Google Forms-like React UI: builder, renderer, submissions, and analytics, powered by your own database.
@knurdz/lava-form-builder · plug-in components · optional Sheets / email / analytics plugins
| Surface | Export | What it does |
|---|---|---|
| Admin builder | FormBuilder |
Drag-reorder fields, settings, banners, integrations toggles |
| Public fill | FormRenderer |
Multi-page forms, team roster, drafts, file uploads |
| Responses | FormSubmissions |
Filter, paginate, optimistic detail drawer |
| Insights | FormAnalyticsCharts |
Trends, breakdowns, summary stats |
Persistence is your job: implement LavaFormStore against Postgres, Appwrite, Firebase, or anything else. The library ships createMemoryStore() for demos and tests.
npm install @knurdz/lava-form-builderPeers: react and react-dom (^18.2 or ^19).
Styles auto-inject when you import the package. To load CSS yourself:
import "@knurdz/lava-form-builder/styles.css";import {
FormBuilder,
FormRenderer,
createMemoryStore,
createSubmitAction,
getFormAvailability,
} from "@knurdz/lava-form-builder";
const store = createMemoryStore({ publicFileBaseUrl: "https://cdn.example.com" });
const actions = {
createForm: async (_prev, formData) => {
await store.createForm({
slug: String(formData.get("slug")),
title: String(formData.get("title")),
description: null,
kind: "default",
sortOrder: 0,
});
return { status: "success", message: "Form created.", toastKey: Date.now() };
},
// updateSettings, deleteForm, uploadBanner, deleteBanner, bulkSaveFields …
};
export function AdminForms({ form, forms }) {
const submit = createSubmitAction(store, async () => form);
return (
<>
<FormBuilder
forms={forms}
selectedForm={form}
bannerUrl={null}
googleSheetsConnection={null}
googleSheetsOAuthConfigured={false}
publicFormBaseUrl="forms.example.com"
actions={actions}
/>
{form ? (
<FormRenderer
form={form}
availability={getFormAvailability(form)}
slug={form.slug}
submit={submit}
/>
) : null}
</>
);
}Full wiring sketch: examples/basic-usage.tsx.
┌─────────────────────────────────────────────────────────┐
│ Host app (auth, routes, revalidation) │
│ └── LavaFormStore implementation (your database) │
└───────────────────────────┬─────────────────────────────┘
│
┌───────────────────────────▼─────────────────────────────┐
│ @knurdz/lava-form-builder │
│ FormBuilder · FormRenderer · FormSubmissions · Charts │
│ submitForm() · buildFormOverview() · validation/layout │
└─────────────────────────────────────────────────────────┘
type LavaFormPlugins = {
sheets?: LavaFormSheetsPlugin; // Google Sheets sync after submit
email?: LavaFormEmailPlugin; // Confirmation email + contacts
analytics?: LavaFormAnalyticsPlugin;
};Pass plugins to submitForm(store, form, formData, plugins) or createSubmitAction. If a plugin is missing, related builder UI stays disabled or hidden.
Wrap surfaces in .lfb-root and override CSS variables:
| Variable | Role |
|---|---|
--lfb-bg |
Surface background |
--lfb-fg |
Primary text |
--lfb-border |
Borders |
--lfb-muted |
Secondary text |
--lfb-accent |
Focus / links |
Internal Tailwind utilities are compiled into styles.css; hosts do not need Tailwind installed.
The submission drawer creates #lava-form-builder-drawer-portal on document.body when needed (full-viewport overlay).
- Bump
versioninpackage.jsononmain. - Draft Release workflow opens/updates a draft GitHub Release.
- Publish the release → Publish Package ships to npm (OIDC, public) and GitHub Packages (
npm publish --access public).
If a GitHub Packages version was already published as private, open the package on GitHub → Package settings → Change visibility → Public (one-time for that package).
Repo: github.com/knurdz/lava-form-builder
MIT · Knurdz