Docs/customization/Customization

Customization

This guide covers how to customize NextShip for your specific needs.

Theming

Color Scheme

Theme colors are configured in src/config/themes.ts:

export const theme = {
  // Choose a preset: blue, green, purple, orange, rose, teal, zinc
  primary: presets.green,
 
  // Border radius: sm, md, lg, xl
  radius: "lg",
 
  // Enable dark mode toggle
  enableDarkMode: true,
};

Available Presets

PresetLightDark
blueoklch(0.546 0.245 262)oklch(0.623 0.214 259)
greenoklch(0.527 0.154 150)oklch(0.627 0.194 149)
purpleoklch(0.553 0.235 303)oklch(0.627 0.265 303)
orangeoklch(0.705 0.213 47)oklch(0.705 0.213 47)
roseoklch(0.645 0.246 16)oklch(0.645 0.246 16)

Custom Colors

Add custom colors in src/app/globals.css:

:root {
  --primary-light: oklch(0.5 0.2 250);
  --primary-dark: oklch(0.6 0.2 250);
}

Site Configuration

Update src/config/site.ts:

export const siteConfig = {
  name: "Your SaaS",
  description: "Your SaaS description",
  url: process.env.NEXT_PUBLIC_APP_URL,
  links: {
    twitter: "https://twitter.com/yourhandle",
    github: "https://github.com/yourrepo",
  },
};

Subscription Plans

Modify plans in src/config/plans.ts:

export const plans = {
  starter: {
    name: "Starter",
    price: { monthly: 9, yearly: 90 },
    stripePriceId: {
      monthly: "price_xxx",
      yearly: "price_yyy",
    },
    limits: {
      projects: 5,
      storage: "1GB",
    },
    features: [
      "5 projects",
      "1GB storage",
      "Email support",
    ],
  },
  // Add more plans...
};

Internationalization

Adding a New Language

  1. Add locale to src/i18n/config.ts:
export const locales = ["en", "zh", "es"] as const;
export const localeNames = {
  en: "English",
  zh: "中文",
  es: "Español",
};
  1. Create translation file src/messages/es.json:
{
  "common": {
    "signIn": "Iniciar sesión",
    "signUp": "Registrarse"
  }
}

Using Translations

import { useTranslations } from "next-intl";
 
function Component() {
  const t = useTranslations("common");
  return <button>{t("signIn")}</button>;
}

Adding Components

Using shadcn/ui CLI

npx shadcn@latest add button
npx shadcn@latest add card
npx shadcn@latest add dialog

Components are added to src/components/ui/.

Creating Custom Components

// src/components/shared/feature-card.tsx
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
 
interface FeatureCardProps {
  icon: React.ReactNode;
  title: string;
  description: string;
}
 
export function FeatureCard({ icon, title, description }: FeatureCardProps) {
  return (
    <Card>
      <CardHeader>
        <div className="mb-2 text-primary">{icon}</div>
        <CardTitle>{title}</CardTitle>
      </CardHeader>
      <CardContent>
        <p className="text-muted-foreground">{description}</p>
      </CardContent>
    </Card>
  );
}

Extending the Database

Adding Tables

  1. Define in src/lib/db/schema.ts:
export const products = pgTable("products", {
  id: text("id").primaryKey(),
  name: text("name").notNull(),
  price: integer("price").notNull(),
  createdAt: timestamp("created_at").defaultNow(),
});
  1. Push changes:
pnpm db:push

Adding API Routes

Create tRPC router in src/server/trpc/routers/:

// src/server/trpc/routers/product.ts
export const productRouter = createTRPCRouter({
  list: publicProcedure.query(({ ctx }) => {
    return ctx.db.select().from(products);
  }),
});

Removing Features

Remove Stripe

  1. Delete src/app/api/stripe/
  2. Remove billing components
  3. Remove Stripe environment variables
  4. Uninstall: pnpm remove stripe @stripe/stripe-js

Remove Internationalization

  1. Remove [locale] from route structure
  2. Delete src/i18n/ and src/messages/
  3. Remove next-intl configuration
  4. Uninstall: pnpm remove next-intl

Best Practices

  • Keep site config in src/config/
  • Use environment variables for secrets
  • Create reusable components in src/components/shared/
  • Document customizations in your own README