Complete guide to configuring ShipSafe's centralized configuration system.
Overview
ShipSafe uses a single config.ts file at the project root to centralize all application-wide settings. This makes the codebase predictable, maintainable, and easy to customize. All components and features reference this single source of truth.
Key benefits:
- Single source of truth - All settings in one place
- Type-safe - Full TypeScript support
- Easy customization - Change once, updates everywhere
- No hardcoded values - Components read from config
Configuration File Location
The main configuration file is located at:
config.ts (project root)
This is the single source of truth for all application configuration.
Configuration Sections
Basic App Information
appName: "ShipSafe",
appDescription: "A security-first Next.js boilerplate...",
domainName: "shipsafe.st",
Fields:
appName- Your application name (used in SEO, emails, UI)appDescription- App description (used in SEO meta tags)domainName- Production domain (no https://, no trailing slash)
Customization:
appName: "My Awesome SaaS",
appDescription: "The best SaaS platform for...",
domainName: "myawesomesaaS.com",
Support & Contact
supportEmail: "support@shipsafe.st",
Fields:
supportEmail- Public support email (shown in footer, contact pages, email replies)
Customization:
supportEmail: "help@myawesomesaaS.com",
Email Configuration (Resend)
ShipSafe uses Resend for all transactional emails. Email configuration is handled via environment variables:
email: {
get fromEmail() {
return `ShipSafe <no-reply@${config.domainName}>`;
},
get replyTo() {
return config.supportEmail;
},
},
Environment Variables Required:
RESEND_API_KEY- Your Resend API key (get from resend.com)RESEND_FROM_EMAIL- Optional: override default from email
Customization:
The email configuration automatically uses your domainName and supportEmail from config. To customize:
- Set environment variable:
RESEND_API_KEY=re_xxxxxxxxxxxxx
RESEND_FROM_EMAIL="Your App <noreply@yourdomain.com>" # Optional
- Email is automatically configured - No code changes needed!
See Email Documentation for complete email setup guide.
Billing (Stripe Plans)
stripe: {
plans: [
{
priceId: process.env.STRIPE_PRICE_STARTER || "",
name: "Starter",
description: "Essential tools to launch your SaaS securely.",
price: 99,
priceAnchor: 199, // Optional: original price
isFeatured: false,
features: [
{ name: "Firebase Authentication" },
{ name: "Firestore Integration" },
// ... more features
],
},
{
isFeatured: true, // Only one featured plan
priceId: process.env.STRIPE_PRICE_PRO || "",
name: "Pro",
price: 199,
features: [...],
},
],
}
Plan Fields:
priceId- Stripe Price ID from environment variablesname- Plan name (displayed in pricing table)description- Plan descriptionprice- Monthly price in dollars (for display)priceAnchor- Optional original price (shows crossed out)isFeatured- Boolean to highlight this plan (only one should be true)features- Array of feature objects withnameproperty
Customization:
stripe: {
plans: [
{
priceId: process.env.STRIPE_PRICE_BASIC || "",
name: "Basic",
description: "Perfect for individuals",
price: 29,
features: [
{ name: "10 projects" },
{ name: "Basic support" },
],
},
{
isFeatured: true,
priceId: process.env.STRIPE_PRICE_PREMIUM || "",
name: "Premium",
price: 99,
priceAnchor: 149, // Shows "was $149, now $99"
features: [
{ name: "Unlimited projects" },
{ name: "Priority support" },
{ name: "Advanced features" },
],
},
],
}
Note: Price IDs come from .env.local to keep Stripe credentials separate.
UI Theme Settings
colors: {
theme: "dark",
main: "hsl(var(--p))", // Uses DaisyUI primary color
},
Fields:
theme- DaisyUI theme name (must match theme intailwind.config.ts)main- Main accent color (used for progress bars, browser tab, etc.)
Available Themes:
dark(default)lightcupcakebumblebeeemeraldcorporatesynthwaveretrocyberpunkvalentinehalloweengardenforestaqualofipastelfantasywireframeblackluxurydraculacmykautumnbusinessacidlemonadenightcoffeewinter
Customization:
colors: {
theme: "light", // Change theme
main: "#3b82f6", // Custom HEX color
},
To add custom themes, see Custom Themes Guide.
Authentication Routes
auth: {
loginUrl: "/auth",
callbackUrl: "/dashboard",
logoutRedirect: "/",
},
Fields:
loginUrl- Where unauthenticated users are redirectedcallbackUrl- Where users land after successful loginlogoutRedirect- Where users go after logout
Customization:
auth: {
loginUrl: "/login", // Custom login page
callbackUrl: "/app", // Custom dashboard route
logoutRedirect: "/goodbye", // Custom logout page
},
Complete Configuration Example
Here's a complete example configuration:
const config = {
// Basic Info
appName: "My Awesome SaaS",
appDescription: "The best SaaS platform for managing your business.",
domainName: "myawesomesaaS.com",
// Support
supportEmail: "help@myawesomesaaS.com",
// Email (Resend)
// Email configuration is automatic - uses domainName and supportEmail
// Set RESEND_API_KEY in .env.local
// Billing
stripe: {
plans: [
{
priceId: process.env.STRIPE_PRICE_BASIC || "",
name: "Basic",
price: 29,
features: [
{ name: "10 projects" },
{ name: "Email support" },
],
},
{
isFeatured: true,
priceId: process.env.STRIPE_PRICE_PRO || "",
name: "Pro",
price: 99,
features: [
{ name: "Unlimited projects" },
{ name: "Priority support" },
],
},
],
},
// Theme
colors: {
theme: "light",
main: "#3b82f6",
},
// Auth
auth: {
loginUrl: "/auth",
callbackUrl: "/dashboard",
logoutRedirect: "/",
},
};
export default config;
Using Configuration in Components
Import and Use
import config from "@/config";
export default function MyComponent() {
return (
<div>
<h1>Welcome to {config.appName}</h1>
<p>{config.appDescription}</p>
</div>
);
}
Accessing Plans
import config from "@/config";
export default function PricingPage() {
const featuredPlan = config.stripe.plans.find((plan) => plan.isFeatured);
return (
<div>
<h2>Our Featured Plan: {featuredPlan.name}</h2>
</div>
);
}
Using in Server Components
Configuration works in both client and server components:
// Server Component (default)
import config from "@/config";
export default async function Page() {
return <h1>{config.appName}</h1>;
}
// Client Component
"use client";
import config from "@/config";
export default function ClientPage() {
return <h1>{config.appName}</h1>;
}
Best Practices
1. Never Hardcode Values
❌ Bad:
<h1>Welcome to ShipSafe</h1>
✅ Good:
<h1>Welcome to {config.appName}</h1>
2. Use Environment Variables for Secrets
❌ Bad:
stripe: {
secretKey: "sk_live_123...", // Never do this!
}
✅ Good:
stripe: {
plans: [
{
priceId: process.env.STRIPE_PRICE_STARTER || "", // From .env
},
],
}
3. Keep Config Single Source
- Don't duplicate config values in components
- Always import from
@/config - Update config once, changes everywhere
4. Type Safety
Config is fully typed. TypeScript will catch errors:
// ❌ Type error: featuredPlan doesn't exist
const bad = config.stripe.plans.find((p) => p.isFeatured2);
// ✅ Correct
const good = config.stripe.plans.find((p) => p.isFeatured);
Customization Workflow
Step 1: Update Basic Info
appName: "Your App Name",
appDescription: "Your app description",
domainName: "yourdomain.com",
Step 2: Update Support Email
supportEmail: "support@yourdomain.com",
Step 3: Configure Plans
- Create plans in Stripe Dashboard
- Add Price IDs to
.env.local - Update
config.tswith plan details
Step 4: Customize Theme
- Choose DaisyUI theme or create custom
- Update
colors.themein config - Update
tailwind.config.tsif needed
Step 5: Adjust Auth Routes
Update redirect URLs if you have custom routes.
Environment Variables
Configuration can reference environment variables for sensitive values:
stripe: {
plans: [
{
priceId: process.env.STRIPE_PRICE_STARTER || "", // From .env.local
},
],
},
Email Environment Variables:
RESEND_API_KEY- Required for sending emailsRESEND_FROM_EMAIL- Optional: override default from address
Note: For required values, validate in your code or use environment variable validation. See Environment Variables Guide for complete setup.
Troubleshooting
Config Not Updating
Issue: Changes to config not reflecting
Solutions:
- Restart dev server - Config is loaded at startup
- Check syntax - Ensure valid TypeScript/JavaScript
- Verify import - Use
@/configalias, not relative path
Type Errors
Issue: TypeScript errors when using config
Solutions:
- Check types - Ensure config matches expected types
- Restart TypeScript server - In VS Code: Cmd+Shift+P → "TypeScript: Restart TS Server"
- Verify config export - Must use
export default config
Learn More
- Environment Variables - Configure secrets
- Stripe Setup - Set up Stripe plans
- Custom Themes - Customize theme
- Project Structure - Understand codebase structure