Next.js Starter
GitHub

Ship faster with a
modern foundation

Stop wasting time on boilerplate. Start with authentication, database, file storage, and a scalable architecture — all pre-configured and ready to deploy.

Everything you need, nothing you don't

Authentication that just works

Sign up, sign in, password reset — handled. Reactive state with useSyncExternalStore keeps your UI in sync instantly, while secure cookies enable seamless server-side access.

Architecture that scales

Built on Feature-Sliced Design principles. Clear boundaries between features, widgets, and shared code mean your codebase stays organized as your team and product grow.

Forms without the pain

Zod schemas + React Hook Form = type-safe validation from frontend to backend. Define once, validate everywhere. No more runtime surprises.

Own your infrastructure

Deploy anywhere with Docker Compose. Optimized for Coolify, but works with any container platform. Your data, your servers, your rules.

File uploads, simplified

Pre-configured Cloudflare R2 client for S3-compatible storage. Upload files, generate presigned URLs, and manage assets without vendor lock-in.

Backend you can extend

PocketBase gives you a real-time database, auth, and file storage in a single binary. Add custom endpoints and hooks with JavaScript when you need more.

Up and running in 2 minutes

1

Clone the repository

Use the GitHub template or clone directly to get started.

git clone https://github.com/karnak19/next-starter.git my-app
cd my-app
2

Install dependencies & start services

Bun handles package installation. Docker Compose starts PocketBase.

bun install
docker compose -f docker-compose.yml -f docker-compose.dev.yml up -d
# PocketBase is now running at http://localhost:8080
3

Configure & launch

Copy the environment template and start the dev server.

cp .env.local.example .env.local
bun dev
# Open http://localhost:3000

See how it works

Client-side Authentication

The useAuth hook gives you reactive access to the current user. No prop drilling, no context boilerplate — just import and use.

  • Automatic hydration handling prevents UI flicker
  • Real-time updates when auth state changes
  • Type-safe user object from PocketBase
my-component.tsx
import { useAuth } from "@/shared/providers/auth-provider";

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

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

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

Sign In & Sign Out

Authentication actions are simple async functions. Call them from anywhere — form handlers, buttons, or effects.

  • Cookie sync keeps server and client in sync
  • Automatic error handling with typed responses
  • Works with Server Components out of the box
auth-actions.ts
import { signIn, signOut, syncAuthCookie } from "@/shared/auth/client";

// Sign in with email and password
async function handleSignIn(email: string, password: string) {
  await signIn(email, password);
  syncAuthCookie(); // Sync to cookie for SSR
}

// Sign out clears everything
function handleSignOut() {
  signOut(); // Clears auth store + cookie
}

Server-side Auth Access

Server Components can read auth state from cookies. Perfect for protecting pages, fetching user-specific data, or rendering personalized content.

  • Zero client JavaScript for auth checks
  • Secure — tokens never exposed to client
  • Works with Next.js middleware
page.tsx (Server Component)
import { cookies } from "next/headers";
import PocketBase from "pocketbase";

export default async function ProtectedPage() {
  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;
  // Now use `user` to fetch data or render UI
}

Type-safe Form Validation

Define your schema once with Zod, and get type inference everywhere. React Hook Form handles the rest — validation, errors, and submission.

  • Compile-time type checking
  • Automatic error messages
  • Reuse schemas on server for API validation
sign-in-form.tsx
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";

const schema = z.object({
  email: z.string().email("Invalid email"),
  password: z.string().min(8, "Min 8 characters"),
});

type FormData = z.infer<typeof schema>;

function SignInForm() {
  const { register, handleSubmit, formState } = useForm<FormData>({
    resolver: zodResolver(schema),
  });
  // formState.errors is fully typed!
}

Organized from day one

Feature-Sliced Design

A battle-tested architecture that keeps your code organized as complexity grows. Each layer has clear responsibilities and import rules.

  • features/ User interactions and business logic (auth forms, etc.)
  • widgets/ Composite UI blocks that combine features (user menu, etc.)
  • shared/ Reusable utilities, UI components, and API clients
src/
├── features/         # User interactions
│   └── auth/
│       ├── sign-in-form.tsx
│       ├── sign-up-form.tsx
│       └── schemas.ts
├── widgets/          # Composite blocks
│   ├── user-menu/
│   └── health-status/
└── shared/           # Shared code
    ├── ui/           # shadcn components
    ├── db/           # PocketBase clients
    ├── auth/         # Auth helpers
    ├── storage/      # R2 client
    ├── providers/    # React providers
    └── lib/          # Utilities

Frequently asked questions

Why PocketBase instead of a traditional database?

PocketBase gives you a database, authentication, file storage, and real-time subscriptions in a single 15MB binary. No separate services to manage, no complex setup. And don't let anyone tell you SQLite doesn't scale — it handles thousands of concurrent users without breaking a sweat. PocketBase is production-ready for the vast majority of applications, not just MVPs.

Can I use this with a different database?

Technically yes, but that's not the point of this starter. If you want Prisma, Drizzle, or another ORM, there are plenty of excellent starters built around those. This one is specifically designed around PocketBase's strengths — use it if that's what you want.

Is Feature-Sliced Design overkill for small projects?

We don't follow FSD to the letter — just the parts that make sense. Three folders (features,widgets , shared) with simple import rules. No layers, no segments, no over-engineering. It's lightweight structure that grows with you, not a rigid framework.

Why Bun instead of Node.js?

Bun is significantly faster for installing dependencies and running scripts. It's also a drop-in replacement for Node.js in most cases. If you prefer Node, just replace bun with npm or pnpm in the scripts — everything else works the same.

How do I add OAuth providers (Google, GitHub, etc.)?

PocketBase has built-in OAuth2 support. Enable providers in the PocketBase admin UI at /_/, add your client ID and secret, and use the pb.collection('users').authWithOAuth2() method. The auth provider in this starter will pick up the session automatically.

Can I use this for a SaaS with multiple tenants?

Yes, but you'll need to add tenant isolation. PocketBase supports collection rules that can filter data by tenant ID. For more complex multi-tenancy (separate databases per tenant), you might want to look at a different architecture, but this starter is a solid foundation for single-tenant or simple multi-tenant apps.

Is Cloudflare R2 required for file storage?

No. PocketBase has built-in file storage that works great for most use cases. R2 is pre-configured as an option for when you need S3-compatible storage, CDN distribution, or want to separate file storage from your database server.

How do I customize the UI components?

All UI components are from shadcn/ui, which means they live in your codebase (not node_modules). Edit them directly in src/shared/ui/. They're built with Radix primitives and Tailwind, so customization is straightforward.

Ready to build something great?

Join developers who've stopped reinventing the wheel and started shipping faster.