Skip to main content
This guide covers all configuration options for Coffee Finder, including Next.js settings, database configuration, Tailwind CSS customization, and environment variables.

Next.js configuration

The Next.js configuration is defined in next.config.ts at the project root:
next.config.ts
import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  output: "standalone",
  typescript: {
    ignoreBuildErrors: true,
  },
  reactStrictMode: false,
};

export default nextConfig;

Configuration options

Enables standalone output mode for optimized production builds. This creates a minimal server with all dependencies bundled.Benefits:
  • Smaller Docker images
  • Faster deployments
  • Self-contained deployment artifacts
The build script copies static files after build:
package.json
"build": "next build && cp -r .next/static .next/standalone/.next/ && cp -r public .next/standalone/"
Set to true for flexible development. TypeScript errors won’t block builds.For production:
next.config.ts
typescript: {
  ignoreBuildErrors: false, // Enable strict checking
},
Run type checking manually:
bun run lint
Currently set to false. Enable for additional development checks:
next.config.ts
reactStrictMode: true,
Strict mode helps identify potential problems but may cause components to render twice in development.

Additional configuration options

You can extend next.config.ts with additional options:
next.config.ts
const nextConfig: NextConfig = {
  output: "standalone",
  typescript: {
    ignoreBuildErrors: true,
  },
  reactStrictMode: false,
  
  // Image optimization
  images: {
    domains: ['example.com'],
    formats: ['image/avif', 'image/webp'],
  },
  
  // Custom headers
  async headers() {
    return [
      {
        source: '/api/:path*',
        headers: [
          { key: 'Access-Control-Allow-Origin', value: '*' },
        ],
      },
    ];
  },
  
  // Rewrites for API proxying
  async rewrites() {
    return [
      {
        source: '/api/external/:path*',
        destination: 'https://external-api.com/:path*',
      },
    ];
  },
};

TypeScript configuration

The TypeScript configuration is defined in tsconfig.json:
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "noImplicitAny": false,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "react-jsx",
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ],
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    ".next/types/**/*.ts"
  ],
  "exclude": ["node_modules"]
}

Path aliases

The @/* alias maps to the src directory:
// Instead of:
import { Button } from '../../../components/ui/button'

// Use:
import { Button } from '@/components/ui/button'
Add custom path aliases to simplify imports throughout your application.

Tailwind CSS configuration

Tailwind CSS is configured in tailwind.config.ts:
tailwind.config.ts
import type { Config } from "tailwindcss";
import tailwindcssAnimate from "tailwindcss-animate";

const config: Config = {
  darkMode: "class",
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",
  ],
  theme: {
    extend: {
      colors: {
        background: 'hsl(var(--background))',
        foreground: 'hsl(var(--foreground))',
        card: {
          DEFAULT: 'hsl(var(--card))',
          foreground: 'hsl(var(--card-foreground))'
        },
        // ... more color definitions
      },
      borderRadius: {
        lg: 'var(--radius)',
        md: 'calc(var(--radius) - 2px)',
        sm: 'calc(var(--radius) - 4px)'
      }
    }
  },
  plugins: [tailwindcssAnimate],
};

export default config;

Dark mode

Coffee Finder uses class-based dark mode:
tailwind.config.ts
darkMode: "class"
Toggle dark mode by adding the dark class to the <html> element. This is handled automatically by next-themes.

CSS variables

Colors are defined using CSS variables in src/app/globals.css:
globals.css
:root {
  --radius: 0.625rem;
  --background: oklch(1 0 0);
  --foreground: oklch(0.145 0 0);
  --primary: oklch(0.205 0 0);
  --primary-foreground: oklch(0.985 0 0);
  /* ... more variables */
}

.dark {
  --background: oklch(0.145 0 0);
  --foreground: oklch(0.985 0 0);
  --primary: oklch(0.922 0 0);
  --primary-foreground: oklch(0.205 0 0);
  /* ... dark mode variables */
}
Learn how to customize these colors in the Customization guide.

Database configuration

The database is configured using Prisma in prisma/schema.prisma:
prisma/schema.prisma
generator client {
  provider = "prisma-client-js"
}

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

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

model Post {
  id        String   @id @default(cuid())
  title     String
  content   String?
  published Boolean  @default(false)
  authorId  String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

Switching to PostgreSQL

To use PostgreSQL instead of SQLite:
1

Update Prisma schema

prisma/schema.prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}
2

Update environment variable

.env
DATABASE_URL="postgresql://user:password@localhost:5432/coffee_finder?schema=public"
3

Install PostgreSQL client

bun add @prisma/client
4

Run migrations

bun run db:migrate

Prisma client configuration

The Prisma client is initialized in src/lib/db.ts:
src/lib/db.ts
import { PrismaClient } from '@prisma/client'

const globalForPrisma = globalThis as unknown as {
  prisma: PrismaClient | undefined
}

export const db =
  globalForPrisma.prisma ??
  new PrismaClient({
    log: ['query'], // Log all queries in development
  })

if (process.env.NODE_ENV !== 'production') 
  globalForPrisma.prisma = db
Configure logging for different environments:
// Development: verbose logging
new PrismaClient({
  log: ['query', 'info', 'warn', 'error'],
})

// Production: errors only
new PrismaClient({
  log: ['error'],
})

// Disable all logging
new PrismaClient({
  log: [],
})

Environment variables

Coffee Finder uses environment variables for configuration. Create a .env file:
.env
# Required
DATABASE_URL="file:./db/custom.db"

Environment variable reference

VariableRequiredDescriptionDefault
DATABASE_URLYesPrisma database connection stringfile:./db/custom.db
Coffee Finder uses SQLite by default. For production deployments, you can switch to PostgreSQL or MySQL by updating the DATABASE_URL and the provider in prisma/schema.prisma.

Environment-specific files

Create separate environment files for different stages:
.env                  # Default (committed to git as template)
.env.local            # Local overrides (gitignored)
.env.development      # Development environment
.env.production       # Production environment
.env.test             # Test environment
Next.js automatically loads the correct file based on NODE_ENV.

shadcn/ui configuration

The UI component library is configured in components.json:
components.json
{
  "$schema": "https://ui.shadcn.com/schema.json",
  "style": "new-york",
  "rsc": true,
  "tsx": true,
  "tailwind": {
    "config": "",
    "css": "src/app/globals.css",
    "baseColor": "neutral",
    "cssVariables": true,
    "prefix": ""
  },
  "aliases": {
    "components": "@/components",
    "utils": "@/lib/utils",
    "ui": "@/components/ui",
    "lib": "@/lib",
    "hooks": "@/hooks"
  },
  "iconLibrary": "lucide"
}

Adding new components

Install additional shadcn/ui components:
bun x shadcn@latest add button
bun x shadcn@latest add dialog
bun x shadcn@latest add dropdown-menu
Browse available components at ui.shadcn.com.

API configuration

The coffee shop search API is configured in src/app/api/coffee-shops/route.ts:
src/app/api/coffee-shops/route.ts
export async function GET(request: NextRequest) {
  const { searchParams } = new URL(request.url)
  const lat = searchParams.get('lat')
  const lng = searchParams.get('lng')
  const radius = searchParams.get('radius') || '8000'

  // Query Overpass API for coffee shops
  const response = await fetch(
    'https://overpass-api.de/api/interpreter',
    {
      method: 'POST',
      body: overpassQuery,
      next: { revalidate: 120 }, // Cache for 2 minutes
    }
  )
  // ...
}

Adjust search parameters

Modify default search behavior:
// Increase search radius
const radius = searchParams.get('radius') || '15000' // 15km

// Adjust cache duration
next: { revalidate: 300 } // 5 minutes

// Increase timeout
[out:json][timeout:60]; // 60 seconds

Map configuration

Leaflet map styles are configured in src/app/globals.css:
globals.css
.coffee-finder-user-pin div {
  width: 20px;
  height: 20px;
  border-radius: 9999px;
  background-color: var(--primary);
  border: 3px solid var(--background);
}

.coffee-finder-shop-pin div {
  width: 18px;
  height: 18px;
  border-radius: 9999px;
  background-color: var(--chart-1);
  border: 1px solid var(--foreground);
}
Customize pin sizes, colors, and styles by modifying these classes.

Next steps

With your configuration complete:

Build docs developers (and LLMs) love