Skip to content

The client site starter for TanStack Start and Supabase.

JED gives you sign-in with two-step login, roles enforced in Postgres, an admin your client can run, and an API with an MCP server. Start each project from the same tested base and spend your time on the parts that are actually new.

Features

What you get on day one

Each of these works in the demo and is covered in the docs.

  • Sign-in and two-step login

    Email and Google sign-in, plus TOTP codes you can require for admins or for everyone.

  • Roles enforced by RLS

    Permissions live in Postgres, so the database refuses what the app shouldn't allow.

  • Admin panel and editor

    Pages and posts in a BlockNote editor with drafts, so clients can update the site without you.

  • Blog with RSS

    Public post list, post pages, and an RSS feed, all listed in the sitemap.

  • Media library

    Upload and reuse images from the admin, with signed upload tickets on the server.

  • Search

    Postgres full-text search for pages and posts, and Ctrl K to jump through the docs.

  • Contact inbox

    Messages are stored in an admin inbox, with optional email delivery through Resend.

  • Branding settings

    Site name, accent colour, logo, and navbar and footer width, changed from settings.

  • SQL templates

    Every migration with a plain-English summary and an applied or not-applied badge.

  • REST API with OpenAPI

    Read and write endpoints under /api/v1, described in an OpenAPI spec you can generate clients from.

  • MCP server

    Connect an AI client to /api/mcp. Writes need confirm: true and an idempotency key.

  • Tokens and audit log

    Scoped tokens run as their owner under RLS, and every call is written to the audit log.

  • Security headers

    CSP and CSRF protection are on by default, and session pages are never cached publicly.

  • CI and tests

    Every pull request runs Biome, typecheck, tests, a build, and npm audit.

Admin dashboard

Dashboard

Spring updatePublished
ServicesDraft
AboutPublished

Block editor

AboutDraft saved

About

We design and build gardens for homes across the valley.

Type / for blocks

API token

Name
Static build
Expires in days
90
amn_k9f2...Active
Create token

MCP config

{
  "mcpServers": {
    "jed": {
      "url": "https://site.example/api/mcp",
      "headers": {
        "Authorization": "Bearer amn_..."
      }
    }
  }
}

Sign-in

Sign in

Use your email and password, then your authenticator code.

you@example.com
********
Sign in

Settings

Site name
Valley Gardens
Timezone
Asia/Manila
DiscardSave

Auth and roles

Access control that holds up when someone skips the UI

Sign-in runs on Supabase Auth with cookie sessions checked on the server. New accounts start as members. Admin access comes from a roles table, and row level security checks the same permissions on every query, so a bug in a page can't hand out data the database wouldn't. Turn on TOTP two-step login with one of four policies: off, optional, required for admins, or required for everyone.

How auth and roles work

Admin

An admin your client can use without calling you

Pages and blog posts are written in a BlockNote editor with drafts and previews. Next to them sit the media library, users, redirects, the contact inbox, the audit log, and settings for branding and layout. Each screen checks its own permission, so you can hand out access screen by screen.

Tour the admin panel

API and MCP

One token model for scripts and AI clients

The REST API under /api/v1 and the MCP server at /api/mcp run the same handlers. A personal access token carries the scopes you pick, never more than its owner has, and each call runs as that person under RLS. Every call lands in the audit log. MCP writes need confirm: true and an idempotency key, so a retried call can't apply a change twice.

Read the API docs

Shipping

Checks on every pull request

CI runs Biome, the TypeScript check, the Vitest suite, a production build, and npm audit on every pull request. CSP headers, CSRF protection on server functions, and private cache headers on session routes are set before you write a line.

Deploying to Vercel

In the box

What's in the repository

  • TanStack Start app with React 19, file routes, and server functions
  • Supabase migrations for profiles, roles, content, media, contact messages, and API tokens, all with RLS
  • Sign-in, sign-up, password reset, Google sign-in, and TOTP enrolment and challenge pages
  • Admin: dashboard, pages, blog, media, users, redirects, contact inbox, audit log, SQL templates, settings, and API tokens
  • Public pages: home, features, docs, blog with RSS, search, contact, and sitemap.xml
  • REST API under /api/v1 with an OpenAPI spec, and an MCP server at /api/mcp
  • Vercel deployment through the Nitro preset
  • GitHub Actions CI and a Vitest suite
  • .env.example with every variable explained

Who it's for

Built for developers who ship sites for clients

If you build a new marketing site, blog, or small product site every few weeks, you rebuild the same sign-in, admin, and content model each time. JED is that layer, already tested. Clone it for each client, connect a fresh Supabase project, and deploy to Vercel. Each client keeps their own code, database, and deployment.

It's a good fit if you're comfortable with TypeScript, React, and SQL migrations. It's not a hosted CMS or a no-code builder.

Stack

Tools you already know

  • TanStack Start

    React 19 and Vite, with file routes, loaders, and server functions

  • Supabase

    Postgres, Auth, Storage, and row level security

  • Vercel

    Deploys through the Nitro Vercel preset, with a preview for every pull request

  • Tailwind and shadcn/ui

    Design tokens and accessible components you can restyle per client

  • Zod

    Validation at every server boundary and for the API

  • Biome and Vitest

    Lint, format, and tests, run in CI

Getting started

From clone to first admin

Most setups take a few minutes once your Supabase project exists.

01

Clone the repository

Clone JED into a new folder for the client and run npm ci.

02

Connect Supabase

Run npm run setup, then link your Supabase project and push the migrations.

03

Grant an admin and brand it

Make your account an admin with one SQL insert, then set the name, colour, and logo in settings.

Terminal
$ git clone https://github.com/jahzlariosa/jed.git my-site
$ cd my-site
$ npm ci
$ npm run setup

Then follow the setup guide to apply the schema and create your first admin.

Questions

Is JED a hosted CMS?
No. It's a code starter. You clone it for each client and host it on your own Vercel and Supabase accounts.
Do I have to use Vercel?
The build is set up for Vercel with the Nitro Vercel preset, and the docs cover that path. Other hosts would need a different Nitro preset.
How do I bring starter updates into an existing client site?
Add the starter as a git remote, fetch it, and merge or cherry-pick what you need. Then apply any new migrations with npx supabase db push.
Can an AI assistant break the site through MCP?
A token can't do more than its owner, every query runs under RLS, and write tools only run with confirm: true and an idempotency key. Every call is in the audit log, and you can revoke a token at any time.
Does the contact form need an email service?
No. Messages are saved to the admin inbox. Add a Resend API key and sender address if you also want them emailed.
Can I drop features I don't need?
Yes. It's your code. Remove the routes and admin screens you don't use, and keep the migrations in order.

Start the next client site from JED

The setup guide takes you from an empty repository to a running site with an admin account.