Skip to content

A production-ready Next.js + Elysia starter with Feature-Sliced Design architecture.

Notifications You must be signed in to change notification settings

Karnak19/next-starter

Repository files navigation

Next.js Modern Starter

A production-ready Next.js starter with Feature-Sliced Design architecture, designed for self-hosting with Coolify.

Tech Stack

Getting Started

Prerequisites

Setup

  1. Install dependencies

    bun install
  2. Start PocketBase

    docker compose up -d

    This starts PocketBase on port 8080.

  3. Configure environment variables

    cp .env.local.example .env.local

    Update the values as needed. The defaults work for local development.

  4. Start dev server

    bun dev

Open http://localhost:3000 to see the app.

PocketBase Admin UI: http://localhost:8080/_/

Stopping services:

docker compose down

Project Structure

├── app/                    # Next.js App Router (routing only)
├── pocketbase/             # PocketBase extensions
│   ├── pb_hooks/           # Custom JS hooks and routes
│   └── pb_migrations/      # Database migrations
├── src/                    # Feature-Sliced Design layers
│   ├── features/           # User interactions (auth forms, etc.)
│   ├── widgets/            # Composite UI blocks (user menu, etc.)
│   └── shared/             # Shared utilities and clients
│       ├── auth/           # Auth helpers (client & server)
│       ├── db/             # PocketBase clients (browser, server, admin)
│       ├── providers/      # React providers (auth, theme, query)
│       ├── storage/        # R2 storage client
│       ├── lib/            # Utilities (cn, query client)
│       └── ui/             # shadcn/ui components
└── public/                 # Static assets

Scripts

bun dev           # Start dev server
bun build         # Build for production
bun start         # Start production server
bun lint          # Run Biome linter
bun format        # Format code with Biome

Environment Variables

See .env.local.example for all variables. Local development defaults:

# PocketBase
POCKETBASE_URL=http://127.0.0.1:8080

# R2 Storage (optional)
R2_ACCOUNT_ID=your-account-id
R2_ACCESS_KEY_ID=your-access-key
R2_SECRET_ACCESS_KEY=your-secret-key
R2_BUCKET_NAME=your-bucket

Production with Docker Compose: Use the service name instead of localhost for internal communication:

# Server-side (internal Docker network)
POCKETBASE_URL=http://pocketbase:8080

# Client-side (public URL)
NEXT_PUBLIC_POCKETBASE_URL=https://api.yourdomain.com

Authentication

This starter uses PocketBase for authentication with a reactive auth provider.

Client-side Auth

import { useAuth } from "@/shared/providers/auth-provider";

function MyComponent() {
  const { user, isAuthenticated, isLoading } = useAuth();

  if (isLoading) return <Skeleton />;
  if (!isAuthenticated) return <SignInPrompt />;

  return <div>Welcome, {user.name}!</div>;
}

Sign In / Sign Out

import { signIn, signOut, syncAuthCookie } from "@/shared/auth/client";

// Sign in
await signIn(email, password);
syncAuthCookie(); // Sync to cookie for server-side access

// Sign out
signOut(); // Clears auth store and cookie

Server-side Auth

Server components can read auth state from cookies:

import { cookies } from "next/headers";
import PocketBase from "pocketbase";

const cookieStore = await cookies();
const pb = new PocketBase(process.env.POCKETBASE_URL);

const authCookie = cookieStore.get("pb_auth");
if (authCookie?.value) {
  pb.authStore.loadFromCookie(`pb_auth=${authCookie.value}`);
}

const user = pb.authStore.isValid ? pb.authStore.record : null;

PocketBase Clients

Three PocketBase clients are available for different contexts:

Client Import Use Case Singleton Safe?
pbBrowser @/shared/db/browser Client components (via proxy) Yes (browser)
pbServer @/shared/db/server Server components (read-only) No*
pbAdmin @/shared/db/admin Server Actions (superuser) Yes

Important: pbAdmin is the recommended way to interact with PocketBase from server-side code (Server Actions, API routes, webhooks). It authenticates as a superuser and is safe to use as a singleton because it doesn't track regular user auth state.

As recommended by the PocketBase maintainer:

"You could create one-off node server-side actions that will interact with PocketBase only as admin/superuser and as pure data store"

*pbServer creates a new instance per request in server components to safely read user-specific data via cookies.

Extending PocketBase

PocketBase can be extended with JavaScript hooks in pocketbase/pb_hooks/. Files are automatically reloaded on changes.

For full documentation, see PocketBase JS Overview.

TypeScript Support

Add the reference directive for IDE autocompletion:

/// <reference path="../pb_data/types.d.ts" />

routerAdd("GET", "/api/example", (e) => {
  // Full type hints available
});

Features

  • Reactive Auth: AuthProvider with useSyncExternalStore for instant UI updates
  • Server/Client Auth Sync: Cookie-based auth sync between client and server
  • Loading States: Built-in hydration handling to prevent UI flicker
  • Type-safe Forms: Zod schemas with React Hook Form
  • Streaming: React Suspense with server components
  • File Storage: R2-compatible storage client
  • Extensible Backend: Custom routes and hooks via PocketBase JS
  • Self-hosted Ready: Docker Compose setup designed for Coolify deployment

About

A production-ready Next.js + Elysia starter with Feature-Sliced Design architecture.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •