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
| Preset | Light | Dark |
|---|---|---|
| blue | oklch(0.546 0.245 262) | oklch(0.623 0.214 259) |
| green | oklch(0.527 0.154 150) | oklch(0.627 0.194 149) |
| purple | oklch(0.553 0.235 303) | oklch(0.627 0.265 303) |
| orange | oklch(0.705 0.213 47) | oklch(0.705 0.213 47) |
| rose | oklch(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
- Add locale to
src/i18n/config.ts:
export const locales = ["en", "zh", "es"] as const;
export const localeNames = {
en: "English",
zh: "中文",
es: "Español",
};- 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 dialogComponents 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
- 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(),
});- Push changes:
pnpm db:pushAdding 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
- Delete
src/app/api/stripe/ - Remove billing components
- Remove Stripe environment variables
- Uninstall:
pnpm remove stripe @stripe/stripe-js
Remove Internationalization
- Remove
[locale]from route structure - Delete
src/i18n/andsrc/messages/ - Remove
next-intlconfiguration - 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