# Fillo install reference

Last updated: 2026-06-22

Canonical human page: https://fillo.so/docs/embed
Agent entrypoint: https://fillo.so/llms.txt

## Short answer

Fillo renders forms as native controls inside the host app, not as an iframe or hosted-page detour. Use `@usefillo/react` in React or Next.js apps. Use `@usefillo/dom` for Vue, Svelte, Astro, web components, plain browser JavaScript, or any other frontend. Fillo handles validation, conditional logic, file uploads, responses, CSV export, webhooks, and later edits.

## Choose the package

| App surface | Package | Primary API |
| --- | --- | --- |
| React or Next.js | @usefillo/react | <FilloForm /> |
| Custom React layout | @usefillo/react | <FilloProvider />, <FormField />, useFillo(), useField() |
| Vue, Svelte, Astro, vanilla browser JavaScript | @usefillo/dom | renderForm() |
| No build step | @usefillo/dom | standalone.global.js or the web component |

## Install for React or Next.js

```bash
npm install @usefillo/react@latest
```

```tsx
import { FilloForm } from "@usefillo/react";
import "@usefillo/react/styles.css";

export function Feedback() {
  return <FilloForm formId="FORM_ID_OR_SLUG" />;
}
```

Published forms can be rendered by form id or slug. Draft forms are not served by the public API.

## Create a new form from code

Use `defineForm` for code-owned forms. Always pass a `client` if the form should collect responses.

```tsx
import { FilloForm, createClient, defineForm } from "@usefillo/react";
import "@usefillo/react/styles.css";

const client = createClient({ key: process.env.NEXT_PUBLIC_FILLO_KEY! });

const feedback = defineForm({
  id: "product-feedback",
  title: "Product feedback",
  pages: [{ id: "p1", blocks: [
    { id: "email", kind: "email", label: "Work email", required: true },
    { id: "score", kind: "linear_scale", label: "How was this?", min: 0, max: 10 },
    { id: "notes", kind: "long_text", label: "What should we know?" },
  ]}],
});

export function ProductFeedback() {
  return <FilloForm form={feedback} client={client} />;
}
```

## Create a live workspace with the CLI

Use this path when the app does not have a Fillo workspace yet. The CLI can create a capped preview workspace, push the form live, and print the form id plus a claim URL.

```bash
npx @usefillo/cli@latest init
npx @usefillo/cli@latest push waitlist.json --handle beta-waitlist
```

If the developer already has a Fillo account, use `npx @usefillo/cli@latest login` instead of `init`.

## Install for other frameworks

```bash
npm install @usefillo/dom@latest
```

```ts
import { createClient, defineForm, renderForm } from "@usefillo/dom";
import "@usefillo/dom/styles.css";

const client = createClient({ key: "pk_your_key" });
const form = defineForm({
  id: "contact",
  title: "Contact",
  pages: [{ id: "p1", blocks: [
    { id: "email", kind: "email", label: "Email", required: true },
    { id: "message", kind: "long_text", label: "Message" },
  ]}],
});

const instance = renderForm(document.querySelector("#fillo-form"), {
  form,
  client,
  onSubmitted: (id, data) => console.log("response", id, data),
});

// instance.setValue("email", "jane@example.com");
// await instance.submit();
// instance.destroy();
```

## No build step

```html
<link rel="stylesheet" href="https://unpkg.com/@usefillo/dom@latest/dist/styles.css" />
<script src="https://unpkg.com/@usefillo/dom@latest/dist/standalone.global.js"></script>

<div id="fillo-form"></div>
<script>
  const client = Fillo.createClient({ key: "pk_your_key" });
  const form = Fillo.defineForm({
    id: "contact",
    title: "Contact",
    pages: [{ id: "p1", blocks: [
      { id: "email", kind: "email", label: "Email", required: true },
      { id: "message", kind: "long_text", label: "Message" },
    ]}],
  });

  Fillo.renderForm(document.querySelector("#fillo-form"), { form, client });
</script>
```

## Responses and files

- With a `client`, responses post to Fillo and appear in the dashboard.
- `onSubmitted(id, data)` runs app-side follow-up code after Fillo records the response.
- Use webhooks to deliver responses to another backend.
- File uploads can go to connected storage such as Google Drive, Box, or S3-compatible storage while the response keeps the file reference.
- Without a `client`, a code-defined form is render-only and will not save responses.

## Styling

- Import the default stylesheet unless the app already has intentional form styling.
- Use the `theme` prop for quick tokens: `{ colorScheme, primary, background, text, radius, fontFamily }`.
- Use `theme={{ colorScheme: "dark" }}` when the form renders on a known dark surface or the host app uses class-based dark mode.
- Write CSS or Tailwind against `.fillo-*` classes. The SDK default CSS is in a lower cascade layer, so host app rules win.
- Each field has `data-field`; each choice row has `data-option`.

## Auto-submit feedback forms

For one-tap article feedback, CSAT, or inline pulse checks, set `settings.submitMode` to `"auto"`. The default renderer hides the submit button for complete discrete answers, then brings it back when the answer opens a text, upload, or custom follow-up. Add `submissionLimit: "once_per_visitor"` for browser-scoped one-response feedback.

```tsx
const articleVote = defineForm({
  id: "article-vote",
  title: "Was this helpful?",
  pages: [{ id: "p1", blocks: [
    { id: "vote", kind: "select", label: "Was this helpful?", required: true,
      options: [
        { id: "up", label: "Yes", icon: "thumbs_up" },
        { id: "down", label: "No", icon: "thumbs_down" },
      ] },
    { id: "unclear", kind: "long_text", label: "What was unclear?",
      visibleIf: [{ fieldId: "vote", op: "eq", value: "down" }] },
  ]}],
  settings: { submitMode: "auto", submissionLimit: "once_per_visitor" },
});

<FilloForm form={articleVote} client={client} showTitle={false} />;
```

## Headless layout control

Use `<FilloProvider>` when the app must own the whole layout. It runs the form engine but renders no default layout. Place fields with `<FormField id="...">`, render custom markup between fields, and drive page state or submit with `useFillo()`.

```tsx
import { FilloProvider, FormField, useFillo } from "@usefillo/react";

<FilloProvider form={feedback} client={client}>
  <FormField id="email" />
  <YourProductCallout />
  <FormField id="notes" />
  <SubmitButton />
</FilloProvider>;

function SubmitButton() {
  const form = useFillo();
  return <button onClick={() => void form.submit()}>Send</button>;
}
```

## Field kinds

Field kinds: short_text, long_text, email, url, phone, number, select, multi_select, dropdown, checkbox, rating, linear_scale, ranking, matrix, signature, date, file_upload, hidden, custom.

Content blocks: heading, paragraph, divider.
