Affix is located at the bottom of the screen, scroll to see it

Documentation Overview

Overview of the documentation. Learn how to build accessible, production-ready apps step by step — from setup to deployment.

Get started

Hey indie ! Welcome ʕ•ᴥ•ʔ

Start a local server

In your terminal

#run the following commands one-by-one

git clone [email protected]:br0wsa/a11ystart-boilerplate.git [MYAPP]
cd [MYAPP]
pnpm install
git remote remove origin
pnpm dev
  1. Rename .env.example to .env

  2. Open http://localhost:3000 to see your dev mod app

  3. To start preview server run pnpm preview command

  4. Open http://localhost:8788 to see your preview mod app

File Structure

You may want to define several pages/ directories for what we call a domain-driven file structure (DDD).

  • public
  • components
  • pages
  • layouts
  • database
  • assets
  • atoms
  • .cursor
  • server
  • package.json
  • wrangler.toml
  • .env
  • ...others

Config files

All there is to know about how Vike's config files work. Further more, These are key configuration files for your project. Rename them with your app name - same everywhere.

{
  "name": "a11ystart",
  "version": "1.0.0-beta.1",
  "description": "a11y saas boilerplate",
  "author": {
    "name": "bluecornflakes",
    "email": "[email protected]",
    "url": "https://www.bluecornflakes.com"
  },
  "homepage": "https://a11ystart.blue/",
  "keywords": [
    "a11y",
    "low-tech",
    "user-experience",
    "accessibility-first",
    "empowerment",
    "community-building"
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/br0wsa/a11ystart-boilerplate"
  },
  "license": "Proprietary",
  "licenseFile": "LICENSE"
}

Environment Variables

Environment variables can be defined in .env and .env.[mode] files.

# ------------------------------
#      ENVIRONMENT EXAMPLE
# ------------------------------

# APP URL
PUBLIC_ENV__URL=


# BEEHIIV Publication ID API
BEEHIIV_PUBLICATION_ID=
BEEHIIV_API_KEY=
PUBLIC_ENV__BEEHIIV_RSS_URL=
PUBLIC_ENV__BEEHIIV_AFFILIATE_URL=


# GitHub API
PUBLIC_ENV__GITHUB_TOKEN=

# Cloudflare
CLOUDFLARE_API_TOKEN=
CLOUDFLARE_ACCOUNT_ID=
CLOUDFLARE_EMAIL=

# Google Analytics
# Docs: https://support.google.com/analytics/answer/9304153
PUBLIC_ENV__GOOGLE_ANALYTICS=

# Sentry DNS. Used for Error Reporting on the Server
SENTRY_DSN=

# Sentry DNS. Used for Error Reporting in the Browser
PUBLIC_ENV__SENTRY_DSN=

# Resend
RESEND_API_KEY=


# Supabase
SUPABASE_URL=
SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=

# Cloudflare Turnstile
# Cloudflare Turnstile
CLOUDFLARE_TURNSTILE_SECRET_KEY=
PUBLIC_ENV__CLOUDFLARE_TURNSTILE_SITE_KEY=

# IPQualityScore
IPQUALITYSCORE_API_KEY=

# Stripe
STRIPE_SECRET_KEY=
PUBLIC_ENV__CUSTOMER_PORTAL_LINK=

# Sightengine
SIGHTENGINE_USER=
SIGHTENGINE_SECRET=
SIGHTENGINE_WORKFLOW_ID=

Features

Includes database integration, email handling, SEO optimization, error boundaries, customer support tools, payment support, analytics, and magic link authentication.

SEO

Add your root URL (e.g. https://yourdomain.com) to baseUrl in vite.config.ts (located in the root folder). This will automatically generate a sitemap.xml and robots.txt during the build process.

// Vite Sitemap Config
// Adds sitemap.xml and robots.txt at build time.

sitemap({
  baseUrl: "https://a11ystart.blue",
  filename: "sitemap.xml",
  defaultChangefreq: "weekly",
  defaultPriority: 0.5,
  robots: { userAgent: "*", disallow: { cloudflare: true } },
  outputDir: "../../public",
}),
  1. Update your content files in the /database folder: schemaOrg.ts, faq.ts, glossary.ts, legal.ts, contact.ts, etc. Customize them to reflect your business.

  2. Add your Google Analytics ID as PUBLIC_ENV__GOOGLE_ANALYTICS in your .env file.

  3. Add your site URL as PUBLIC_ENV__URL in your .env file.

  4. Create a newsletter in minutes using Beehiiv— with beehiiv page or your subdomain.

  5. Add your RSS feed URL from beehiiv RSS as PUBLIC_ENV__BEEHIIV_RSS_URL in your .env file.

All of the following SEO snippets are already integrated in the boilerplate. Simply reuse them when adding new pages.

// Breadcrumb JSON-LD
// This is already implemented in the boilerplate.
// Include this snippet in +Head.tsx for each page.

export function Head() {
  const data = useData<Data>();
  const PAGE_JSON_LD = generateJsonLd(data);

  return (
    <script
      type="application/ld+json"
      dangerouslySetInnerHTML={{
        __html: JSON.stringify(PAGE_JSON_LD),
      }}
    />
  );
}

Supabase

Copy these SQL queries from the root server/ folder.

  • server
    • templates
      • users_table_setup.sql
      • user_tos_acceptance.sql
      • user_account_deleted.sql
      • email_notifications.sql
      • cyclic-coupon-batch.sql
      • color_table.sql
      • helpers.sql

Sending emails

A transactional email provider like Resend is required to send magic links, password resets, and system notifications. Ensure your provider supports SMTP or API integration.

  1. Create a Resend account.

  2. Navigate to Domains+ Add Domain. Use a subdomain like resend.yourdomain.com.

  3. Complete DNS verification, then click Verify DNS Records in Resend.

  4. Go to API Keys+ Create API Key, then click Add.

  5. Open your .env file and add your RESEND_API_KEY to enable email sending with React Email on the contact/support page.

  6. In Supabase → Edge Functions → Secret, store the API key RESEND_API_KEY.

  7. In Supabase → Authentication → Email settings, enable Custom SMTP and configure the following:

    • Sender details

    • SMTP Provider Settings

  • Send emails from a subdomain (e.g., resend.domain.com)

  • Add an SPF record for the subdomain

  • Add a DKIM record for the subdomain

  • Add a DMARC record for the subdomain

  • Include an unsubscribe link at the bottom of each email

  • Personalize emails using the recipient’s name

  • Avoid spam-trigger words like "free" or "earn money"

  • Avoid strange fonts, inconsistent layout, or excessive punctuation

  • Avoid linking to untrusted or suspicious websites

Payments

Copy these SQL queries from the root server/ folder.

  • server
    • templates
      • stripe.sql
    • edge-functions
      • handleStripeWebhook.ts
  1. Create a Stripe account and activate payments.

  2. In [Settings] → [Public Details], add your website URL.

  3. In [Settings] → [Branding], add your logo and colors.

  4. Enable payment and refund emails in [Customer Emails].

  5. Optionally enable the customer portal in [Customer Portal].

  6. Under [Rules], enable 3DS and block on CVC failure.

  7. Turn ON Test Mode.

  8. Create a product and copy the price ID into StripeBtn.tsx.

  9. Add your API key to .env.local as STRIPE_SECRET_KEY.

  10. Add PUBLIC_ENV__CUSTOMER_PORTAL_LINK to your .env file to allow users to manage their subscription and Stripe account via a dedicated portal.

  11. Use Stripe CLI: stripe listen --forward-to localhost:3000/api/webhook/stripe

  12. If using coupons, define them in server/controllers/stripe.ts

  13. In Supabase, enable Vault and the Stripe Wrapper extension.

  14. Define a new schema and name it : Stripe in your wrapper.

  15. In your Supabase edge function, include STRIPE_SECRET_KEY and add the secret.

  16. Deploy a new edge functions to handle Stripe webhook, past handleStripeWebhook.ts and deploy it.

  17. In Supabase editor run stripe.sql

Customer support

Integrate live chat into your website using Cloudflare without writing any code.

  1. Create a Crisp account .

  2. Go to the Crisp Cloudflare Integration page and click Install the App.

  3. Enter your Crisp Website ID when prompted.

  4. Once installed, the chat widget is automatically embedded across your website, with no code required.

Error handling

Error tracking is already integrated using Sentry and a global event listener. You just need to provide your Sentry credentials to enable reporting.

# Sentry configuration
SENTRY_AUTH_TOKEN=
SENTRY_ORG=
SENTRY_PROJECT=
PUBLIC_ENV__SENTRY_DSN=
  1. Fill in the required Sentry environment variables in your .env file.

  2. The function sentryBrowserConfig() is already invoked in sentry.browser.config.ts to initialize Sentry on the frontend.

  3. A global window.addEventListener("error") logs unhandled exceptions to the console.

  4. A custom error page is defined at /pages/_error/+Page.js. It’s automatically shown when an error occurs or when throw render(abortStatusCode) is called.

  5. Developer tooling is pre-configured with tsconfig.json, eslint.config.js, and .prettierrc already set up for a clean DX.

Analytics

Paste the tracking Google tag in .env files.

# Google Analytics
# Docs: https://support.google.com/analytics/answer/9304153
PUBLIC_ENV__GOOGLE_ANALYTICS=

Security

Keep your a11yStart app secure from common threats — safety first!

Sending limit in Resend

Monthly budget in OpenAI

Rate limiting (API Routes)

Security Headers

  • server

Complete the following security headers according to your needs to improve your app’s security.

Schema Validation

Even in TypeScript, runtime validation matters. Use Zod to protect your API routes from invalid payloads.

import { Hono } from 'hono';
import { z } from 'zod';

const app = new Hono();

const schema = z.object({
  username: z.string().min(3),
  age: z.number().int().positive(),
});

app.post('/user', async (c) => {
  const data = await c.req.json();
  const parsed = schema.safeParse(data);

  if (!parsed.success) {
    return c.json({ error: 'Invalid data' }, 400);
  }

  return c.json({ message: 'User created' });
});

Deployment

Build & deploy serverless functions, sites, and full-stack applications.

Workers & Pages

  1. Go to Workers & Pages → Create
  2. Select Pages as your deployment target
  3. Choose Import a Git repository → Get Started
  4. Follow the setup steps provided by Cloudflare
  5. Sync your local .env usingpnpm sync:env in the terminal

Extras

Extra tools and tips

Setup

Learn more about the metadata file convention.

  • Add your logo to the /assets folder as icon.svg.
  • Create a 1200×660 image for social media previews. Name it previewImage.jpg and place it in the /assets folder.
  • Generate your site favicon using a free favicon generator. Download the ZIP and extract its content into the /public folder.

Useful external ressources

Follow updates

Stay in the loop with the latest updates and news 🚀📢

Get notified on GitHub

Follow a11yStart on GitHub