docs/contributing/development.mdx
This guide walks you through setting up Reactive Resume for local development. Whether you're contributing to the project or customizing it for your needs, these steps will get you up and running.
```bash
# Install pnpm if you haven't already
npm install -g pnpm
# Install project dependencies
pnpm install
```
```bash
docker compose -f compose.dev.yml up -d
```
This starts the following infrastructure services:
- **PostgreSQL** — Database (port 5432)
- **SeaweedFS** — S3-compatible storage (port 8333)
- **Printer** — PDF and screenshot generation service (port 4000)
- **Mailpit** — Email testing server (SMTP on port 1025, UI on port 8025)
<Tip>
Use `compose.dev.yml` instead of `compose.yml` for local development. The development file only includes infrastructure services with ports exposed to your host machine, while the main `compose.yml` includes the full application stack intended for production deployments.
</Tip>
<Tip>
Wait for all services to be healthy before proceeding. Check with `docker compose -f compose.dev.yml ps`.
</Tip>
```bash
# Server
APP_URL=http://localhost:3000
# Printer (required for local development)
PRINTER_APP_URL=http://host.docker.internal:3000
PRINTER_ENDPOINT=ws://localhost:4000?token=1234567890
# Database
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres
# Authentication
AUTH_SECRET=development-secret-change-in-production
# Storage (SeaweedFS)
S3_ACCESS_KEY_ID=seaweedfs
S3_SECRET_ACCESS_KEY=seaweedfs
S3_ENDPOINT=http://localhost:8333
S3_BUCKET=reactive-resume
S3_FORCE_PATH_STYLE=true
# Email (Mailpit for local development)
SMTP_HOST=localhost
SMTP_PORT=1025
```
<Note>
**PDF Generation Note**: The `PRINTER_APP_URL` variable is required when running Reactive Resume outside of Docker while the printer service is running inside Docker (which is the case when using `compose.dev.yml`). The printer needs to reach your local app to render resumes for PDF generation. Since Docker containers cannot access `localhost` on your host machine directly, you must set `PRINTER_APP_URL` to `http://host.docker.internal:3000`. This special hostname allows Docker containers to communicate with services running on your host machine.
</Note>
<Tip>
**Email Testing**: The development stack includes [Mailpit](https://mailpit.axllent.org/), an email testing tool. All emails sent by the application will be captured and viewable at [http://localhost:8025](http://localhost:8025). No emails will actually be sent to real addresses during development.
</Tip>
```bash
pnpm run db:migrate
```
Your local Reactive Resume instance will be available at [http://localhost:3000](http://localhost:3000).
Here are the most commonly used scripts during development:
| Command | Description |
|---|---|
pnpm run dev | Start the development server with hot reload |
pnpm run build | Build the application for production |
pnpm run start | Start the production server |
pnpm run lint | Run Oxlint linter and formatter |
pnpm run format | Run Oxfmt formatter |
pnpm run typecheck | Run TypeScript type checking |
| Command | Description |
|---|---|
pnpm run db:generate | Generate migration files from schema changes |
pnpm run db:migrate | Apply pending migrations |
pnpm run db:studio | Open Drizzle Studio (database GUI) |
| Command | Description |
|---|---|
pnpm run lingui:extract | Extract translatable strings from code |
| Command | Description |
|---|---|
pnpm run docs:dev | Start the Mintlify docs development server |
Understanding the project structure will help you navigate the codebase:
reactive-resume/
├── src/
│ ├── components/ # Reusable React components
│ │ ├── ui/ # Base UI components (Button, Card, etc.)
│ │ ├── resume/ # Resume-specific components
│ │ └── ...
│ ├── dialogs/ # Modal dialogs
│ ├── hooks/ # Custom React hooks
│ ├── integrations/ # Third-party integrations
│ │ ├── auth/ # Better Auth integration
│ │ ├── drizzle/ # Database schema & utilities
│ │ └── orpc/ # API routes & services
│ ├── routes/ # File-based routing (TanStack Router)
│ │ ├── builder/ # Resume builder pages
│ │ ├── dashboard/ # User dashboard
│ │ ├── auth/ # Authentication pages
│ │ └── ...
│ ├── schema/ # Zod schemas for validation
│ ├── utils/ # Utility functions
│ └── styles/ # Global CSS styles
├── public/ # Static assets
├── locales/ # Translation files (.po format)
├── migrations/ # Database migrations
├── docs/ # Mintlify documentation
└── data/ # Local data (fonts, uploads)
Use Drizzle Studio to explore and manage your database:
pnpm run db:studio
This opens a web-based GUI at https://local.drizzle.studio.
src/integrations/drizzle/schema.tspnpm run db:generate
pnpm run db:migrate
<Warning>Always review generated migrations before applying them, especially when working with existing data.</Warning>
Reactive Resume uses Lingui for internationalization.
Use the t macro for strings or <Trans> component for JSX:
import { t } from "@lingui/core/macro";
import { Trans } from "@lingui/react/macro";
// For plain strings
const message = t`Hello, World!`;
// For JSX content
<Trans>Welcome to Reactive Resume</Trans>;
After adding new translatable text, extract them to the locale files:
pnpm run lingui:extract
Translation files are located in the locales/ directory in .po format.
Uses Oxlint for linting and Oxfmt for formatting:
# Check and auto-fix issues
pnpm run lint:fix
pnpm run format:fix
Run TypeScript type checking:
pnpm run typecheck
<Accordion title="Database connection refused">
Ensure Docker containers are running: ```bash docker compose -f compose.dev.yml ps docker compose -f compose.dev.yml
up -d ``` Check that PostgreSQL is healthy and accessible on port 5432.
</Accordion>
<Accordion title="S3/Storage errors">
Verify SeaweedFS is running and the bucket exists: ```bash docker compose -f compose.dev.yml logs seaweedfs docker
compose -f compose.dev.yml logs seaweedfs_create_bucket ``` If the bucket wasn't created, restart the bucket
creation service: ```bash docker compose -f compose.dev.yml restart seaweedfs_create_bucket ```
</Accordion>
<Accordion title="Type errors after pulling changes">
The route tree may need regeneration. Run the dev server which auto-generates routes: ```bash pnpm run dev ``` Or
run type checking to see specific errors: ```bash pnpm run typecheck ```
</Accordion>