pepa

Getting Started

Get Pepa running and publish your first doc in under ten minutes.

611 words·4 min read

Pepa is an open-source documentation platform you own completely. Clone it, fill it with your content, and ship it — no vendor lock-in, no monthly bill, no login required to read your own docs.

This guide walks you through setup from zero to a live site with your first custom page.

Write in MDX

Markdown plus React components. If you know Markdown, you already know 80% of what you need.

Nav builds itself

No nav config files. The sidebar is generated from your file's frontmatter. Add a file, set three fields, done.

Deploy anywhere

Pepa exports to a folder of static HTML. It runs on GitHub Pages, Netlify, Vercel, S3 — anything that can serve files.


Before you begin

You'll need three things installed on your machine:

Node.js 20 or later

Check your version with node --version. If you need to install or upgrade, download from nodejs.org or use a version manager like nvm or fnm.

pnpm

Pepa uses pnpm as its package manager. If you have Node installed, you already have corepack — just run:

corepack enable
corepack prepare pnpm@latest --activate

Or install pnpm directly: npm install -g pnpm

Git

Check with git --version. Git ships with macOS and most Linux distros. Windows users can get it from git-scm.com.


Set up Pepa

1

Clone the repository

Replace my-docs with whatever you want to call your project:

git clone https://github.com/your-org/pepa my-docs
cd my-docs

Want to push your docs to your own GitHub repo later? Delete the .git folder and run git init to start fresh with your own history.

2

Install dependencies

From inside the my-docs folder:

pnpm install

This installs everything in one shot — the Next.js app, the shared UI package, and all tooling.

3

Start the dev server

cd apps/docs
pnpm dev

Open localhost:3000. You should see this page. Every change you make to an MDX file hot-reloads in the browser instantly.

4

Make it yours

Open apps/docs/pepa.config.ts. This is the one file that controls your site's name and which top-level nav tabs are visible:

const config: PepaConfig = {
  name: "my-docs",          // ← change this to your project name
  sections: [
    { id: "docs",          label: "Docs",          href: "/getting-started", enabled: true },
    { id: "api-reference", label: "API Reference", href: "/api-reference",   enabled: false }, // ← enable when ready
    { id: "cookbook",      label: "Cookbook",      href: "/cookbook/overview", enabled: false },
  ],
  // ...
}

Save the file. The header updates immediately.

5

Write your first doc

Create a new file anywhere under apps/docs/content/docs/. The file path becomes the URL. For example:

content/docs/introduction.mdx  →  localhost:3000/introduction
content/docs/api/auth.mdx      →  localhost:3000/api/auth

Paste this starter frontmatter and save:

---
title: My First Page
description: A short description shown under the title.
type: tutorial
section: docs
nav: Tutorials
order: 1
---
 
Hello, world! This is my first Pepa page.

Your page appears in the sidebar immediately — no restart needed.


How the sidebar works

There is no nav config file. The sidebar is generated entirely from your frontmatter fields:

FieldWhat it does
sectionWhich top-level tab this page belongs to (docs, api-reference, cookbook, contributors)
navThe sidebar group label within that section (Tutorials, Reference, How-to Guides, etc.)
orderPosition within the group — lower numbers appear first
navParentOptional sub-section within a group, creates a collapsible nested group
hidden: trueKeeps the page out of the sidebar (useful for landing pages)

A page with nav: Reference and order: 2 will appear as the second item in the "Reference" group. That's it — no other config needed.

To create a nested sub-section, add navParent: "Authentication" to any page's frontmatter. It groups under a collapsible "Authentication" heading inside whatever nav group the page belongs to.


How the project is organized

pepa/
├── apps/
│   └── docs/
│       ├── content/docs/      ← Your MDX files live here
│       ├── app/               ← Next.js App Router pages and layouts
│       ├── components/        ← Sidebar, TOC, MDX renderer, top tabs
│       ├── lib/               ← nav.ts (builds the sidebar), vars.ts (text variables)
│       ├── scripts/           ← CLI tools: search index, find-replace sweep, llms.txt
│       ├── openapi/           ← Your OpenAPI spec (copied to public/ at build time)
│       └── pepa.config.ts     ← Site name, enabled sections, feature flags
│
└── packages/
    └── ui/
        └── src/               ← Shared components: Callout, Tabs, Steps, Tiles, etc.
            └── snippets/      ← Reusable content blocks (edit once, updates everywhere)

The only folder you'll spend most of your time in is content/docs/. Everything else can stay as-is until you want to customize deeper.


Writing in MDX

MDX is Markdown with superpowers. Everything you already know from Markdown works exactly the same — headings, bold, lists, code blocks, links. MDX just lets you drop in React components when you need richer content.

What does an MDX file look like?
---
title: How to Authenticate
description: Add bearer token auth to your API requests.
type: how-to
section: docs
nav: How-to Guides
order: 1
---
 
## Get your API key
 
Go to your dashboard and copy your key.
 
<Callout type="warning">
  Never commit your API key to version control.
</Callout>
 
## Make your first request
 
```bash
curl https://api.example.com/v1/widgets \
  -H "Authorization: Bearer YOUR_API_KEY"
```
Which components can I use?

All components are globally available — no import statements needed. Just use them directly:

ComponentWhat it's for
<Callout type="tip">Tips, warnings, notes, danger alerts
<Steps><Step title="...">Numbered step sequences
<Tabs><Tab label="...">Multi-tab content (great for multi-language code)
<Tiles><Tile title="...">Card grids for navigation or feature overviews
<Accordion><AccordionItem title="...">Collapsible sections
<Kbd>⌘K</Kbd>Keyboard shortcut styling
<Var name="productName" />Text variables (edit in lib/vars.ts)

See every component in action on the Feature Showcase page.

What's the difference between all the `type` values?

The type field follows the Diátaxis framework — a way of thinking about what kind of content a page is:

  • tutorial — Learning-oriented. Teaches by doing. ("Build your first X")
  • how-to — Task-oriented. Solves a specific problem. ("How to do X")
  • reference — Information-oriented. Facts and specs. ("The X field accepts…")
  • explanation — Understanding-oriented. Answers "why". ("Why Pepa works this way")

You can also use quickstart, cookbook, faq, glossary, changelog, or migration for special content types. The type value doesn't change how the page renders — it's there to help you think clearly about what you're writing.


Build and deploy

When you're ready to publish:

pnpm build

This runs the full pipeline: validates all frontmatter, compiles MDX, generates the search index, and exports a static site into apps/docs/out/. Upload that folder to any static host.

If you're deploying to GitHub Pages at a path like https://you.github.io/my-docs/, uncomment the basePath line in apps/docs/next.config.ts and set it to /my-docs.


Why the schema is your friend

Every MDX file is validated against a schema before the build runs. If you delete a required field or typo a value, the build fails immediately with a clear error pointing at the exact file — instead of silently shipping a broken page.

Try it: delete the title field at the top of this file and save. Watch the terminal. Then add it back.

You're editing a live example. This page is at apps/docs/content/docs/getting-started.mdx. Saving any change here hot-reloads in the browser and re-validates against the schema. It's a safe place to experiment.


Where to go next