# Troubleshoot Fillo

Last updated: 2026-07-03

Canonical human page: https://fillo.so/docs/troubleshooting
Install docs: https://fillo.so/docs/embed.md
Styling docs: https://fillo.so/docs/styling.md
JSX authoring docs: https://fillo.so/docs/authoring.md

## The form renders unstyled under Tailwind v3

**Symptom:** the form works but looks bare — no borders, spacing, or button styling.
**Cause:** `@usefillo/react/styles.css` ships its rules in `@layer components`; any unlayered CSS (Tailwind v3's preflight, most resets) outranks every layered rule, so the default theme silently evaporates.
**Fix:** import `@usefillo/react/styles.unlayered.css` instead.

## The hosted /f page doesn't match my embed

**Symptom:** `appearance.classNames` styling shows in your app but not on the hosted page or dashboard preview.
**Cause:** by design — classNames never sync; the hosted page renders the default renderer plus theme tokens only.
**Fix:** put brand parity in theme tokens (`defineForm({ theme })` or the dashboard theme); keep classNames for embed-only polish.

## The form says "This form isn't live yet" or eats submissions

**Symptom:** a code-defined form renders, but shows "This form isn't live yet" in production (or a draft banner in dev) and refuses responses.
**Cause:** code forms sync as drafts; the server rejects public submissions until a human publishes.
**Fix:** publish the form in the dashboard — the browser console notice links straight to it. Staged schema changes need the same publish step.

## Submit fails with "Couldn't reach the server"

**Symptom:** the form loads or submits with a network error while the rest of the site is fine.
**Cause:** an ad-blocker, corporate firewall, or your site's Content-Security-Policy is blocking requests to fillo.so.
**Fix:** check the browser network tab for the blocked request. If your site sets a CSP, allow `connect-src https://fillo.so`. For ad-blockers/VPNs, retry with them off to confirm.

## Blank form, or "useFillo must be used inside <FilloForm> or <FilloProvider>"

**Symptom:** the form renders nothing, or a component that is clearly inside the provider throws the hook error.
**Cause:** two copies of `@usefillo/react` (or of React itself) in the bundle — context identity differs across copies.
**Fix:** dedupe to a single copy: `npm dedupe` / one version in the lockfile; in monorepos check hoisting and bundler aliases.

## JSX authoring errors

Mistakes in `<Fillo.Form>` authoring throw `FilloJsxError` with a stable code; each message links to its entry at `https://fillo.so/docs/troubleshooting#jsx-<code>`. Authoring guide: https://fillo.so/docs/authoring.md.

### jsx-missing_id

A block, an <Fillo.Option>, or <Fillo.Form> itself has no stable string id (1–128 chars); options also need a label.
**Fix:** Write a permanent id by hand — ids key responses, logic, piping, and sync, so never derive them from position or a counter.

### jsx-duplicate_id

Two blocks share one id. Ids key responses and must be unique across the whole form, pages included.
**Fix:** Rename one. If the form is live, keep the id existing responses were stored under and rename the newcomer.

### jsx-non_fillo_child

Something that isn't a Fillo element sits where the schema expects one — raw text or HTML inside <Fillo.Form>, children on a props-only field, or <Fillo.Option> outside a choice field.
**Fix:** Children define the schema: put text in <Fillo.Paragraph>, layout outside the form (or go headless with FilloProvider), and options inside Select, MultiSelect, Dropdown, or Ranking.

### jsx-wrapper_component

A plain React component was used as a form child. The compiler never renders children, so wrappers are invisible to it.
**Fix:** Reuse via a plain function that returns Fillo elements and call it inline: {contactFields()}.

### jsx-opaque_type

A child's component type is opaque — lazy/memo, or the JSX crossed a server-component boundary and arrived as a reference.
**Fix:** Define the form in a client module ("use client") and pass the compiled value across server/client boundaries, never the JSX.

### jsx-unknown_prop

A prop isn't in that component's schema whitelist. Typos and className/style land here instead of vanishing silently.
**Fix:** Use the props in the component reference; styling lives on <Fillo.Form appearance={…}>, never in the schema.

### jsx-option_prop_conflict

A choice field has both an options prop and <Fillo.Option> children, or a heading/paragraph has both a text prop and children.
**Fix:** Pick one source — both compile identically.

### jsx-page_mix

<Fillo.Form> mixes <Fillo.Page> elements with loose blocks, or a Page/Option appears inside a page.
**Fix:** Children are either all pages or all blocks — wrap the loose blocks in a <Fillo.Page>.

### jsx-text_child_required

A Heading or Paragraph has non-string children (expressions, elements), or no text at all.
**Fix:** Pass plain text as children or the text prop — piping tokens like {{name}} are fine.

### jsx-rendered_inert

React actually rendered a Fillo.* field — it was used outside <Fillo.Form> / Fillo.defineForm(), often from a server component.
**Fix:** Keep fields inside <Fillo.Form>, and define the form in a client module ("use client").
