Building Your Ultimate Next.js Boilerplate

A Developer's Guide to Rapid App Development

·

4 min read

Introduction

As developers, we often find ourselves repeating the same setup processes for new projects. Authentication, database connections, payment integrations, and styling – these are the building blocks of most modern web applications. But what if you could skip this repetitive setup and jump straight into building your app's unique features?

In this blog post, we'll explore how to create a powerful boilerplate using Next.js, Tailwind CSS, shadcn/ui, TypeScript, Firebase, PostgreSQL, Prisma, Clerk, and custom authentication. This setup will serve as your launchpad for rapid app development, allowing you to focus on what truly matters – bringing your ideas to life.

Why Build a Boilerplate?

Before we dive into the technical details, let's understand why a custom boilerplate is crucial:

  1. Time-saving: Eliminate repetitive setup tasks

  2. Consistency: Maintain a standardized structure across projects

  3. Best practices: Incorporate industry-standard patterns and tools

  4. Flexibility: Tailor the stack to your preferences and needs

Tech Stack Overview

Our boilerplate will include:

  • Frontend: Next.js, Tailwind CSS, shadcn/ui

  • Backend: Next.js API routes

  • Database: PostgreSQL with Prisma ORM

  • Authentication: Clerk and custom auth options

  • Payment: Stripe integration

  • TypeScript: For type safety and better developer experience

Folder Structure

Here's an example folder structure for our boilerplate:

my-nextjs-boilerplate/
├── src/
│   ├── app/
│   │   ├── api/
│   │   │   ├── auth/
│   │   │   ├── payments/
│   │   │   └── ...
│   │   ├── (auth)/
│   │   │   ├── login/
│   │   │   ├── register/
│   │   │   └── ...
│   │   ├── dashboard/
│   │   └── ...
│   ├── components/
│   │   ├── ui/
│   │   ├── auth/
│   │   └── ...
│   ├── lib/
│   │   ├── prisma.ts
│   │   ├── stripe.ts
│   │   └── ...
│   ├── styles/
│   │   └── globals.css
│   └── types/
├── prisma/
│   └── schema.prisma
├── public/
├── .env
├── .env.example
├── next.config.js
├── package.json
├── tailwind.config.js
└── tsconfig.json

Step-by-Step Implementation

Let's break down the process of creating this boilerplate:

1. Project Initialization

npx create-next-app@latest my-nextjs-boilerplate
cd my-nextjs-boilerplate

Choose the following options:

  • TypeScript: Yes

  • ESLint: Yes

  • Tailwind CSS: Yes

  • src/ directory: Yes

  • App Router: Yes

2. Install Additional Dependencies

npm install @prisma/client @clerk/nextjs @stripe/stripe-js @stripe/react-stripe-js zod react-hook-form @hookform/resolvers/zod @tanstack/react-query
npm install -D prisma @types/node @types/react @types/react-dom @types/stripe

3. Set Up Tailwind CSS and shadcn/ui

First, initialize shadcn/ui:

npx shadcn-ui@latest init

Then, add some components:

npx shadcn-ui@latest add button card form input

4. Configure Prisma

Initialize Prisma and create your schema:

npx prisma init

Edit prisma/schema.prisma:

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

5. Set Up Authentication with Clerk

Create src/app/api/auth/[...nextauth]/route.ts:

import { authMiddleware } from "@clerk/nextjs";

export default authMiddleware({
  publicRoutes: ["/", "/api/webhooks(.*)"],
});

export const config = {
  matcher: ["/((?!.*\\..*|_next).*)", "/", "/(api|trpc)(.*)"],
};

6. Implement Stripe Integration

Create src/lib/stripe.ts:

import Stripe from 'stripe';

export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: '2022-11-15',
});

7. Create a Custom Hook for Authentication

Create src/hooks/useAuth.ts:

import { useUser } from "@clerk/nextjs";
import { useRouter } from "next/navigation";

export function useAuth() {
  const { isSignedIn, user, isLoaded } = useUser();
  const router = useRouter();

  const signOut = () => {
    // Implement sign out logic
  };

  return { isSignedIn, user, isLoaded, signOut };
}

8. Implement Protected Routes

Create src/components/auth/ProtectedRoute.tsx:

import { useAuth } from "@/hooks/useAuth";
import { useRouter } from "next/navigation";
import { ReactNode } from "react";

export function ProtectedRoute({ children }: { children: ReactNode }) {
  const { isSignedIn, isLoaded } = useAuth();
  const router = useRouter();

  if (!isLoaded) {
    return <div>Loading...</div>;
  }

  if (!isSignedIn) {
    router.push("/login");
    return null;
  }

  return <>{children}</>;
}

9. Set Up Environment Variables

Create a .env file:

DATABASE_URL="postgresql://username:password@localhost:5432/mydb"
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_clerk_publishable_key
CLERK_SECRET_KEY=your_clerk_secret_key
STRIPE_SECRET_KEY=your_stripe_secret_key
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=your_stripe_publishable_key

Conclusion

With this boilerplate, you now have a solid foundation for building modern web applications using Next.js, Tailwind CSS, and a powerful set of tools. This setup allows you to focus on your app's unique features without getting bogged down in repetitive setup tasks.

Remember to customize this boilerplate to fit your specific needs. As you work on projects, continue to refine and expand your boilerplate, adding new components and utilities that you find yourself using frequently.

Happy coding, and may your future projects launch faster than ever before!


Did you find this article valuable?

Support Mikey by becoming a sponsor. Any amount is appreciated!