PLATFORM GUIDE

How to use Stencil

From connecting a GitHub repo and deploying, to building pages, components, conditional logic, and article templates — everything you need to run your site.

01 / SETUP

Set up GitHub

Stencil stores everything as files in a GitHub repository — there's no database. You need a content repo, an OAuth App so people can sign in, and (optionally) a token.

  1. 1

    Create a GitHub repository for your content and make at least one commit (e.g. add a README) — an empty repo has no default branch. Note the owner and name for GITHUB_OWNER and GITHUB_REPO.

  2. 2

    Create an OAuth App for sign-in: GitHub → Settings → Developer settings → OAuth Apps → New OAuth App. Set the Authorization callback URL to <your-site-origin>/auth/github/callback. Copy the Client ID and generate a client secret.

    GITHUB_OAUTH_CLIENT_ID=<client id>
    GITHUB_OAUTH_CLIENT_SECRET=<client secret>
  3. 3

    Roles are derived from each signer's permission on the repo: admin → Admin, maintain → Moderator, write → Editor. Anything below write can't sign in.

  4. 4

    Optional: create a fine-grained token (Developer settings → Personal access tokens → Fine-grained) scoped to the content repo with Contents = Read and write and Metadata = Read. Required if the repo is private and you want anonymous visitors to see the published site; otherwise commits are attributed to whoever is signed in.

    GITHUB_TOKEN=github_pat_...   # optional
  5. 5

    Copy .env.example to .env, fill in the values above, and set a random SESSION_SECRET. Then install and run.

    cp .env.example .env
    npm install
    npm run dev
Open the app, click Sign in, and authorize with GitHub. You're now in the CMS.
02 / DEPLOY

Deploy to Vercel

Deploy the Stencil app (this project) to Vercel. Your content lives in the separate GitHub content repo you set up above.

  1. 1

    Push this project to its own GitHub repository (separate from your content repo).

  2. 2

    In Vercel: New Project → import that repo. The React Router build is detected automatically, and Node 22 is used (pinned in package.json).

  3. 3

    Add the same environment variables from your .env under Project → Settings → Environment Variables: GITHUB_OWNER, GITHUB_REPO, GITHUB_OAUTH_CLIENT_ID, GITHUB_OAUTH_CLIENT_SECRET, SESSION_SECRET, plus optional GITHUB_TOKEN and API_TOKEN.

  4. 4

    Point your OAuth App's callback URL at production: https://<your-domain>/auth/github/callback. An OAuth App allows one callback URL — use a separate app (or a GitHub App) for dev vs prod.

  5. 5

    Deploy. Public pages are edge-cached (s-maxage), and the /content admin routes are already no-store.

03 / BUILD

Create a page

Pages are built with the drag-and-drop visual builder and output clean, self-hosted HTML.

  1. 1

    Go to /content → New Post → choose Page (Visual Builder). Give it a title.

  2. 2

    Drag blocks from the palette (Layout, Basic, Components, Articles) onto the canvas. Select any element to edit its Tailwind classes, inline styles, and attributes in the Properties panel; reorder in the Layers tree.

  3. 3

    Site-wide background and typography come from /content/settings (body classes) and apply to every page.

  4. 4

    Save (commits to the draft branch), then Publish when it's ready.

04 / BUILD

Assign a page to a URL (and override /)

Any page can be served at a custom public path once published — including the site root.

  1. 1

    In the page editor, open Page settings and set the URL Path field, e.g. /about. It serves publicly at that path once published.

  2. 2

    To make a page your home page, set the URL Path to /. The published page assigned to / overrides the default landing page.

  3. 3

    Publish, then open the path in a signed-out browser to confirm it's public. Reserved prefixes (/content, /api, /articles, /embed, /login, …) can't be used as a page path.

05 / BUILD

Create a reusable component

Build a fragment once — a nav, a footer, a call-to-action — and drop it into any page. Edits propagate to every page that uses it.

  1. 1

    Go to /components → + New. Enter a Name and Slug, pick a Category, and leave Type as Static. Create.

  2. 2

    You land in the component editor — build it with the same drag-and-drop blocks.

  3. 3

    In any Page, open the blocks palette → Components category → drag your component in. Update the component later and every page picks up the change.

06 / BUILD

Create a conditional component

A conditional component renders a different branch per visitor, evaluated on the server at request time.

  1. 1

    Go to /components → + New and set Type to “Conditional — show a branch based on rules”. Create.

  2. 2

    In the flow editor, add branches. Each condition is built from signals: auth (logged in, username, roles), query.<param>, data.<key> (page frontmatter), device, time.*, geo.*, and a stable ab.* bucket. Assign a target component to each branch, plus a default fallback.

  3. 3

    Use the test-signals preview to see which branch wins, then Save.

  4. 4

    Drop the conditional component into a page from the Components palette. Pages that use conditionals are served uncached (private, no-store) so per-visitor markup is never shared.

07 / BUILD

Create an article template

Articles (served at /articles/<slug>) render inside a Page you designate as their layout, so every article shares your header, footer, and styling.

  1. 1

    Create a Page for the layout (as in step 03) — add your nav, footer, sidebars, and any surrounding chrome.

  2. 2

    From the palette's Articles category, drag in the Article Content block where the article's header image + body should appear. Publish the page.

  3. 3

    Go to /content/settings → Article Template → select that page. Every article now renders inside it. Pick “Default layout (no template)” to revert to the plain centered layout.

Create an article itself via /content → New Post → Article (a header image is required).