Skip to Content
Headless SDKGetting Started

Getting Started

This guide walks you through creating your first headless funnel project, from installation to deployment.

Prerequisites

  • Node.js 18+download here  if you don’t have it.
  • An AppFunnel account with at least one project created. Sign in at appfunnel.net  and create a project if you haven’t already.

Setup

Install the CLI

Install the AppFunnel CLI globally:

npm install -g appfunnel

Login

Authenticate with your AppFunnel account:

appfunnel login

This opens your browser for OAuth authentication.

Create a project

Scaffold a new funnel project:

appfunnel init

The CLI walks you through interactive prompts to:

  • Select the project to link to
  • Choose a starter template
  • Optionally configure products (select a store, pick prices, and set trial settings)

Start the dev server

Navigate into the project and start developing:

cd my-funnel appfunnel dev

This launches a Vite dev server with hot module replacement on port 5173.

Open in browser

Visit http://localhost:5173  to see your funnel running locally. Edits to your source files are reflected instantly.

Project Structure

After running appfunnel init, your project looks like this:

my-funnel/ ├── appfunnel.config.ts # Funnel configuration ├── tsconfig.json ├── src/ │ ├── funnel.tsx # Layout wrapper (rendered around every page) │ ├── app.css # Global styles (Tailwind) │ └── pages/ │ ├── index.tsx # First page │ ├── loading.tsx # Loading page │ └── result.tsx # Result page └── locales/ └── en.json # Translations
  • appfunnel.config.ts — Defines which project and funnel this code belongs to, along with variables, products, and settings. See Configuration.
  • src/funnel.tsx — A layout wrapper rendered around every page. This is where you add global UI (headers, progress bars, fonts) or your own React context providers.
  • src/pages/*.tsx — Each file is a funnel page. The file name (without extension) becomes the page slug used in routing. Use lowercase with dashes (e.g. choose-goal.tsx, loading.tsx). Nested folders are not supported — all pages must be directly in src/pages/.
  • locales/en.json — Translation strings in { "key": "value" } format. Use {{name}} for interpolation. Access via the useTranslation hook.

funnel.tsx

The funnel wrapper is a plain React component. The SDK handles FunnelProvider and page rendering automatically — you just control the layout:

import './app.css' export default function Funnel({ children }: { children: React.ReactNode }) { return ( <div className="min-h-screen"> {children} </div> ) }

Add shared UI here — a progress bar, a back button, background styling, or any context providers your pages need. The children prop is the current page component.

You do not need to set up FunnelProvider, Vite, or React — the CLI handles all of that. Just edit funnel.tsx for layout and src/pages/*.tsx for page content.

locales/en.json

Translation files are flat JSON with {{variable}} interpolation:

{ "welcome": "Welcome to the quiz", "greeting": "Hello, {{name}}!", "step_counter": "Step {{current}} of {{total}}" }
const { t } = useTranslation() t('greeting', { name: 'John' }) // "Hello, John!"

Your First Page

Every page exports a page definition and a default React component. Here is a minimal example:

import { definePage } from '@appfunnel-dev/sdk' import { useNavigation, useResponse } from '@appfunnel-dev/sdk' export const page = definePage({ name: 'Welcome', routes: [{ to: 'loading' }], }) export default function Welcome() { const { goToNextPage } = useNavigation() const [name, setName] = useResponse<string>('name') return ( <div> <h1>What's your name?</h1> <input value={name ?? ''} onChange={(e) => setName(e.target.value)} /> <button onClick={goToNextPage}>Continue</button> </div> ) }
  • definePage — Declares the page name and its outgoing routes. The to value matches a file name in src/pages/.
  • useNavigation — Provides goToNextPage, goToPage, and other routing helpers.
  • useResponse — Reads and writes a variable scoped to this page. The value is automatically persisted to the session.

Routes are validated at build time. If you reference a page slug that doesn’t exist as a file in src/pages/, the build will fail with a clear error message.

Deploy

When you’re ready to go live, build and publish in two steps:

Build

appfunnel build

This validates all routes and variables, then produces an optimized bundle in dist/.

Publish

appfunnel publish

Uploads the build to AppFunnel hosting. The first publish creates the funnel record and writes the funnelId back to your appfunnel.config.ts so subsequent publishes update the same funnel.

After publishing, link the funnel to a campaign in the AppFunnel dashboard to make it accessible to visitors.

Next Steps

  • Configuration — Customize appfunnel.config.ts with products, locales, and build options.
  • Hooks — Full reference for useVariable, useNavigation, useProducts, and more.
  • Elements — Pre-built interactive components like SingleSelect, Dialog, and SpinnerWheel.
  • Payments — Integrate Stripe or Paddle checkout into your funnel.
Last updated on