Quickstart
Render a Fillo form in your app. Get a form ID, add the component, and keep your own layout around it.
React uses @usefillo/react; Vue, Svelte, Astro, web components, and plain browser pages use @usefillo/dom. The practical renderer, hooks, client methods, settings, and themes are in the SDK reference.
Quickstart
npm install @usefillo/reactA complete, pasteable file. The first line matters: the form is interactive, so in the Next.js App Router, the file must be a Client Component. Passing onSubmitted from a Server Component throws. The form on the right is real: this exact renderer, pointed at a form in our workspace.
"use client"; // the form is interactive — in the Next.js App Router this file must be a Client Component
import { FilloForm } from "@usefillo/react";
import "@usefillo/react/styles.css"; // optional default theme
export function Feedback() {
return (
<FilloForm
formId="your-form-id-or-slug"
onSubmitted={(id) => console.log("response", id)}
/>
);
}Live — try it
Forms must be publishedbefore the public API will serve them. A fetch by ID or slug returns 404 until then. The editor’s live preview runs the same renderer your users get, while your app still controls the surrounding page and styling.
Get a form id
- Build one in the editor. Create a form in the editor (no account needed) or sign up, publish it, and press Sharein the editor’s top bar. It shows the form ID, the hosted link, and a ready-made
<FilloForm formId="…">snippet. The URL slug works anywhere the id does. - Skip the id entirely. A form authored in code. The
<Fillo.Form>ordefineForm()below renders locally without an account or client. Add aclientlater when it should collect responses. - Provision from the terminal. Already have an account? Use
npx @usefillo/cli@latest logininstead ofinit.terminal# No account yet? Start an email-backed preview workspace, # then push the form live. The private workspace link goes to that inbox: npx @usefillo/cli@latest init --email you@company.com npx @usefillo/cli@latest push waitlist.json --handle beta-waitlist # Existing account: npx @usefillo/cli@latest login # Agent-driven setup: npx @usefillo/cli@latest agent bootstrap --email you@company.com # (agent bootstrap also installs the Build with Fillo skill)
Other frameworks
If your app is not React, use @usefillo/dom. It exports the samedefineForm() helper, a framework-agnostic renderForm() function, a web component, and a standalone global bundle for pages without a build step.
npm install @usefillo/domimport { renderForm } from "@usefillo/dom";
import "@usefillo/dom/styles.css";
renderForm("#form", {
formId: "customer-onboarding",
onSubmitted: (id) => console.log("response", id),
});<link rel="stylesheet" href="https://unpkg.com/@usefillo/dom@0.14/dist/styles.css" />
<script src="https://unpkg.com/@usefillo/dom@0.14/dist/standalone.global.js"></script>
<div id="form"></div>
<script>
Fillo.renderForm("#form", { formId: "waitlist" });
</script>See these patterns running in the live examples gallery.
Define a form in code
defineForm() returns one typed schema that renders in any framework. Validation, multi-page logic, and uploads work without opening the visual editor. Pass it to renderForm(), the <fillo-form> element, or <FilloForm form={…}>.
Add a client and responses are collected in your Fillo workspace. Use onSubmitted(id, data) to run your own code the moment a response lands, or webhooks to deliver responses to your own API.
import { defineForm, createClient, renderForm } from "@usefillo/dom";
// defineForm and createClient have the same exports on @usefillo/react and @usefillo/core
const client = createClient({ key: "pk_…" });
// One typed object — validation, pages, logic, and uploads all come from it:
const startProject = defineForm({
id: "start-project",
title: "Start a project",
pages: [{ id: "p1", blocks: [
{ id: "name", kind: "short_text", label: "Project name", required: true },
{ id: "plan", kind: "select", label: "Plan", options: [
{ id: "hobby", label: "Hobby" },
{ id: "team", label: "Team" },
]},
]}],
});
renderForm("#form", { form: startProject, client });
// React: <FilloForm form={startProject} client={client} />In React, you can write the same form as JSX instead. One Fillo.* component per question, compiled to the exact schema defineForm() emits. For the component catalog and rules, read the code-defined forms guide.
"use client";
import { Fillo, createClient } from "@usefillo/react";
import "@usefillo/react/styles.css";
// A client collects responses into your Fillo workspace.
const client = createClient({ key: "pk_…" });
// Inert Fillo.* components — compiled, never rendered, into the exact
// schema defineForm() emits. onSubmitted runs your own code right after
// Fillo records the response:
export function StartProject() {
return (
<Fillo.Form
id="start-project"
title="Start a project"
client={client}
onSubmitted={(id, data) => console.log("response", id, data)}
>
<Fillo.Text id="name" label="Project name" required />
<Fillo.Select id="plan" label="Plan">
<Fillo.Option id="hobby" label="Hobby" />
<Fillo.Option id="team" label="Team" />
</Fillo.Select>
</Fillo.Form>
);
}See a live, working version in the examples gallery.
In a claimed workspace, register new and changed schemas with authenticated `fillo push --stage` or a server sync token; workspaces may optionally allow a publishable key to stage the same drafts. Existing embeds keep resolving registered matching versions, and a human publishes every live change. A capped, unclaimed preview workspace can still apply syncs immediately until claimed. See the full lifecycle.
Point the client at another host
createClient accepts a baseUrl for staging, tests, or a proxy on your own domain. The form sends its requests to that host. If your site sets a Content-Security-Policy, allow it in connect-src, or submissions fail with a network error (see troubleshooting).
import { createClient } from "@usefillo/react"; // same option on @usefillo/dom
const client = createClient({
key: "pk_…",
baseUrl: "https://forms.your-domain.com", // staging, tests, or a proxy on your domain
});Updated