The front office of your SaaS.
Build your website, publish content, support users and send email from one place,
through a block-based page builder, a REST API, an MCP server and a dashboard.
A SaaS needs more than a product. It needs a marketing site, a blog, documentation, a changelog, a help desk and a way to email people, and these usually live in five different tools that don't share content, themes or users. FrontLit is one system for all of it, and it gives apps, AI agents and people one place to work on the same content.
- One content system. Articles, documentation and product updates share the same model and can be placed on any page as data-backed feeds.
- A real page builder. Draft and publish pages from a block-based WYSIWYG editor, themed per team. The editor and renderer are published as React packages you can embed.
- API first. Every capability is defined once in a typed ts-rest contract, which drives server validation, the OpenAPI reference and the typed dashboard client. Nothing ships dashboard-only.
- AI ready. The same capabilities are exposed over a remote MCP server, with OAuth for assistants and API keys for scripts.
- Multi-tenant. Every team gets a subdomain and can attach its own custom domain to its public site.
- Email built in. Lead capture on pages and sending through SendLit, provisioned automatically for each team.
- Open source and portable. Self-host the whole stack with one Docker Compose file.
Run the full stack (PostgreSQL, Redis, SendLit, and FrontLit's API, dashboard and public sites):
git clone https://github.com/codelitdev/frontlit.git
cd frontlit
cp .env.example .env
# Set the required secrets (commands are included as comments in the file).
docker compose up --build -dThen open the dashboard at http://localhost:3000. See Self-hosting for the one-time keys you need to save.
Embed the page builder in your own React app:
npm install @frontlit/page-builderSee the page builder and text editor guides for usage.
Connect an AI agent: add https://api.frontlit.app/mcp as a remote MCP server in your client and sign in. See MCP connections.
| Package | Description | Version |
|---|---|---|
@frontlit/page-builder |
Block-based WYSIWYG page builder and renderer for React | |
@frontlit/text-editor |
TipTap-based rich text editor and static renderer | |
@frontlit/api-contract |
Shared ts-rest contract (Zod schemas and routes), internal | private |
@frontlit/media-uploader |
Media upload dialog with library and Unsplash tabs, internal | private |
The repository also contains the API (apps/api), which owns all product behavior; the dashboard (apps/web), a thin client over the API; the public sites server (apps/sites); and the docs site (apps/docs). A runnable demo of the component packages is in examples/components-demo.
- API (
apps/api): ts-rest routes, MCP tools, PostgreSQL with Drizzle ORM, pgvector for search. - Dashboard (
apps/web): the authenticated team workspace, built on Next.js. - Public sites (
apps/sites): serves each team's published site, resolving the tenant from the request host. - SendLit: the email layer, run alongside FrontLit.
For the full design, see docs/architecture.md.
The root Compose file runs everything: PostgreSQL (FrontLit and SendLit databases), Redis, SendLit, and FrontLit's API, dashboard and public sites.
cp .env.example .env
docker compose up --build -d
docker compose logs frontlit-api
docker compose logs sendlit-apiSet SUPER_ADMIN_EMAIL and SENDLIT_SUPER_ADMIN_EMAIL before the first start. Each process logs a one-time API key; save both. After SendLit prints organizationApiKey, put it in .env as SENDLIT_ORGANIZATION_API_KEY and run docker compose up -d frontlit-api.
| Service | Default URL |
|---|---|
| Dashboard | http://localhost:3000 |
| API | http://localhost:4000 |
| Public sites | http://localhost:3002 |
| SendLit | http://localhost:3001 |
For a public deployment, use externally reachable HTTPS origins and set ENABLE_TRUST_PROXY=true. The self-hosting guide has the details.
Requirements: Bun 1.4.1 or newer and Docker. We develop on Linux; on Windows, use WSL.
bun install
# Postgres, Redis and SendLit. FrontLit itself runs on your machine.
docker compose -f docker-compose.local.yml upWait for FrontLit migrations applied. in the Compose logs, then configure and start the apps in separate terminals (or use bun run dev to run all three together):
cp apps/api/.env.example apps/api/.env
cp apps/web/.env.example apps/web/.env
cp apps/sites/.env.example apps/sites/.env
bun run --filter @frontlit/api-contract build
bun run dev:api # API on http://localhost:4000
bun run dev:web # Dashboard on http://localhost:3000
bun run dev:sites # Public sites on http://localhost:3002
bun run dev:docs # Docs siteDon't chain them with &&: each command waits for the previous process to exit.
On the first SendLit boot, copy the one-time organizationApiKey into apps/api/.env as SENDLIT_ORGANIZATION_API_KEY:
docker compose -f docker-compose.local.yml logs sendlit-apiPostgres is on 127.0.0.1:5433 and SendLit on http://localhost:3001. Don't run this at the same time as the self-host stack, since both bind the same ports.
bun run --filter @frontlit/api-contract build
bun run testOther useful scripts: bun run typecheck, bun run format:check (Biome) and bun run check:drift (verifies the generated auth and billing schemas are up to date). Documentation management and GitHub sync are described in docs/docs-management.md.
ts-rest, PostgreSQL (with pgvector), Drizzle ORM, Next.js, Tailwind CSS, Shadcn UI, and fumadocs for the docs.
ESP settings say SendLit is still provisioning. This means FrontLit has no stored SendLit team key, not that SendLit is down.
-
Check SendLit is up:
curl -s http://localhost:3001/health. -
Check
SENDLIT_API_URLandSENDLIT_ORGANIZATION_API_KEYare set inapps/api/.env(local) or the root.env(self-host). The organization key is printed once on first SendLit boot. Recreating the SendLit database without updating the key causes401 unauthorizedon provisioning. -
Check the outbox in the FrontLit database:
SELECT type, status, attempts, last_error, next_attempt_at FROM outbox_jobs WHERE type = 'provision_sendlit' ORDER BY created_at DESC;
The API requeues failed provisioning jobs on boot and keeps retrying them unless
SENDLIT_PROVISION_RETRY_FOREVER=false. Team admins can also click Retry now on/settings/esp. Restart the API after changing env, then reload the page.
Contributions are welcome. For anything larger than a small fix, please open an issue first so we can agree on the approach.
- Fork the repository and create a branch.
- Make your change, with tests. If you change a published package, add a changeset with
bun run changeset. - Run
bun run lint,bun run format,bun run typecheckandbun run test. - Open a pull request. Tests and code quality checks run on every pull request and must pass.
Public site UI must be built from @frontlit/page-builder primitives; see AGENTS.md for the rules.
Questions are welcome on Discord or in GitHub issues.
Please don't report security issues in public issues. Report them privately through GitHub's security advisories.
FrontLit is split into two licenses:
- AGPL-3.0-or-later covers the server (
apps/api) and the web app (apps/web). If you run a modified FrontLit as a hosted service, you must share your changes under the same license. Self-hosting is free. - MIT covers the packages you embed in your own products:
@frontlit/page-builder,@frontlit/text-editor,@frontlit/media-uploaderand@frontlit/api-contract. Each of these has its ownLICENSEfile.
The AGPL license text is in LICENSE. For a commercial license that allows a private modified fork, contact the maintainers.