Skip to main content
Menu
On this page

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

install
npm install @usefillo/react

A 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.

components/feedback.tsx
"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

  1. 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.
  2. Skip the id entirely. A form authored in code. The <Fillo.Form> or defineForm() below renders locally without an account or client. Add a client later when it should collect responses.
  3. Provision from the terminal. Already have an account? Use npx @usefillo/cli@latest login instead of init.
    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.

install
npm install @usefillo/dom
vite, vue, svelte, or any bundled app
import { renderForm } from "@usefillo/dom";
import "@usefillo/dom/styles.css";

renderForm("#form", {
  formId: "customer-onboarding",
  onSubmitted: (id) => console.log("response", id),
});
plain html
<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.

any framework
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.

jsx - react
"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).

staging, proxy, or your own domain
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

Was this page helpful?