Development Guide

Shipkit is built for developers who want to ship fast. Let's get you set up and building.

Quick Start

Get Shipkit running in 4 steps:

  1. Clone the repo:

    git clone https://github.com/your-repo/shipkit.git
    cd shipkit
    
  2. Install dependencies:

    bun install --frozen-lockfile
    
  3. Set up environment:

    cp .env.example .env.local
    # Edit .env.local with your values
    
  4. Start developing:

    bun dev
    

That's it! You're ready to build.

What You Need

Required Tools

  • Node.js v18+ (download)
  • Bun v1.1+ (install)
  • PostgreSQL v15+ (for database features)
  • VS Code (recommended)

VS Code Setup

Install these extensions for the best experience:

  • Biome - Code formatting and linting
  • Tailwind CSS IntelliSense - CSS autocompletion
  • TypeScript Importer - Smart imports
  • MDX - Markdown with JSX support

Project Structure

Shipkit follows a clean, logical structure:

src/
├── app/                 # Next.js App Router pages
├── components/          # React components
│   ├── ui/             # Reusable UI components
│   └── blocks/         # Page-specific components
├── lib/                # Utility functions
├── server/             # Backend code
│   ├── actions/        # Server actions
│   ├── auth/          # Authentication config
│   └── services/      # Business logic
└── styles/             # Global styles

Development Workflow

Components

  • Server components first - Better performance, fewer bugs
  • Client components only when needed - For interactivity (forms, animations)
  • Organize by purpose - UI components in ui/, page blocks in blocks/

API Routes

  • Server actions for forms and mutations
  • API routes for external integrations
  • Zod validation on all inputs
  • Consistent error handling

Database

  • Drizzle ORM for type-safe queries
  • Migrations for schema changes
  • Transactions for data consistency

Authentication

  • Auth.js v5 for all auth needs
  • Multiple providers supported
  • Server-side sessions preferred

Common Tasks

Add a New Page

# Create app/(app)/my-page/page.tsx
export default function MyPage() {
  return <div>Hello World</div>;
}

Add a Component

// src/components/ui/my-component.tsx
export function MyComponent() {
  return <div>My Component</div>;
}

Add Database Schema

bun run db:generate
# Edit the migration file
bun run db:migrate

Feature Flags

Control features with environment variables:

# Enable Stripe payments
NEXT_PUBLIC_FEATURE_STRIPE_ENABLED=true

# Enable GitHub auth
NEXT_PUBLIC_FEATURE_AUTH_GITHUB_ENABLED=true

Features auto-enable when you add the required env vars.

Design-system lint (@shadcn/lint)

bun run lint:eslint includes @shadcn/lint, an ESLint plugin that checks Tailwind v4 usage against Shipkit's own design system: it reads components.json, src/styles/theme.css, and each component's variants, and reports when a call site restyles a component or hardcodes an off-token value instead of using what the component already offers.

Two rules are enabled, both in eslint.config.mjs:

  • shadcn/no-restyle flags className overrides on design-system components. Contracts (also in eslint.config.mjs) set what each component accepts:
    • Button: layout/margin/width only; padding, height, radius, and type come from size (src/components/ui/button.tsx).
    • CardTitle may resize text, not change font family or weight (src/components/ui/card.tsx).
    • CardContent may change spacing (padding/gap), not typography or color.
    • Every other recognized src/components/ui/** component defaults to layout-only.
  • shadcn/no-arbitrary-values flags off-scale values like p-[13px] in favor of the theme's spacing/radius/color scale (layout values such as w-[320px] are still allowed).

Both rules are set to warn, not error, because existing code already has hundreds of findings and error would fail CI on unrelated, pre-existing code. Fix findings as you touch those files; promote a rule to error once it's clean.

src/components/ui/** and src/components/blocks/** are excluded from ESLint entirely (see the ignores in eslint.config.mjs), so these rules only apply at call sites, not inside the components themselves.

To add or loosen a contract, edit the contracts array under shadcn/no-restyle in eslint.config.mjs. See the design-systems docs for the contract and custom-message format. To silence a specific, reviewed exception:

<Button
  // eslint-disable-next-line shadcn/no-restyle -- reason
  className="p-4"
/>

Before Production

Run these checks:

# Type checking
bun run typecheck

# Code quality
bun run lint

# Tests (if you have them)
bun run test

# Build test
bun run build

Need Help?