---
slug: "shipfree"
source_type: "readme"
source_url: "https://cdn.jsdelivr.net/gh/revokslab/shipfree@main/README.md"
repo: "https://github.com/revokslab/shipfree"
source_file: "README.md"
branch: "main"
---
# ⚡ ShipFree V2

Hi there! 👋

ShipFree is a free alternative to ShipFast, designed to simplify and optimize your shipping process. It’s built using modern web technologies like Next.js, Bun, Stripe, Drizzle ORM, Postgres and Tailwindcss.

## Features

- SEO Optimization
- User authentication with Better-Auth
- Stripe, Polar, Autumn Billing, Dodo Payments, Commet and Creem integration
- Email messaging via Resend, Postmark, Plunk, and Nodemailer
- Modern UI built with Next.js, TailwindCSS, and BaseUI
- Bun as runtime and package manager
- Drizzle ORM and Postgres for database operations
- Internationalization (i18n) with next-intl supporting English, French, and Spanish

## Docs

For full documentation, visit: [ShipFree Docs](https://shipfree.revoks.dev/docs)

## Code of Conduct

Please read our [Code of Conduct](https://github.com/revokslab/shipfree/blob/HEAD/CODE_OF_CONDUCT.md) before contributing.

## Contributing

For people who want to contribute, please refer to [CONTRIBUTING.md](https://github.com/revokslab/shipfree/blob/HEAD/CONTRIBUTING.md).

## Internationalization (i18n) Navigation

ShipFree includes internationalization support using `next-intl` with automatic locale routing. The template supports English (`en`), French (`fr`), and Spanish (`es`) out of the box.

### Navigation Components and Hooks

The `i18n/navigation.ts` file exports internationalized versions of Next.js navigation components and hooks that automatically handle locale prefixes:

- **`Link`** - Internationalized Link component that automatically prefixes routes with the current locale
- **`redirect`** - Server-side redirect function that preserves locale
- **`usePathname`** - Hook that returns the pathname without the locale prefix
- **`useRouter`** - Hook for programmatic navigation with locale support

### Usage Examples

#### Using the Link Component

```tsx
import { Link } from '@/i18n/navigation'

// Automatically includes locale prefix (e.g., /en/about, /fr/about)
<Link href="/about">About</Link>

// Switch to a different locale
<Link href="/about" locale="fr">À propos</Link>
```

#### Using Navigation Hooks

```tsx
'use client'

import { useRouter, usePathname } from '@/i18n/navigation'
import { useLocale } from 'next-intl'

export function MyComponent() {
  const router = useRouter()
  const pathname = usePathname()
  const locale = useLocale()

  // Navigate to a route (automatically includes locale)
  const handleClick = () => {
    router.push('/dashboard')
  }

  // Switch locale for current page
  const switchLanguage = (newLocale: string) => {
    router.replace(pathname, { locale: newLocale })
  }

  return (
    <div>
      <p>Current locale: {locale}</p>
      <p>Current pathname: {pathname}</p>
      <button onClick={handleClick}>Go to Dashboard</button>
      <button onClick={() => switchLanguage('fr')}>Switch to French</button>
    </div>
  )
}
```

#### Using Translations

```tsx
// Server Component
import { getTranslations } from 'next-intl/server'

export default async function ServerPage() {
  const t = await getTranslations('MyPage')
  return <h1>{t('title')}</h1>
}

// Client Component
'use client'
import { useTranslations } from 'next-intl'

export default function ClientPage() {
  const t = useTranslations('MyPage')
  return <h1>{t('title')}</h1>
}
```



### Adding New Locales

To add a new locale:

1. Add the locale to `i18n/routing.ts`:
   ```ts
   locales: ['en', 'fr', 'es', 'de'], // Add 'de' for German
   ```

2. Create a translation file in `messages/`:
   ```json
   // messages/de.json
   {
     "PRICING": "Preise"
   }
   ```

3. Update the `localeNames` object in `components/language-switcher.tsx`:
   ```tsx
   const localeNames: Record<string, string> = {
     en: 'English',
     fr: 'Français',
     es: 'Español',
     de: 'Deutsch', // Add German
   }
   ```

### Route Structure

All routes are automatically prefixed with the locale:
- `/` or `/en` → English homepage
- `/fr` → French homepage
- `/es` → Spanish homepage
- `/en/about` → English about page
- `/fr/about` → French about page

The default locale (`en`) uses the `as-needed` prefix strategy, meaning it doesn't show the locale prefix in the URL when it's the default language.

## Removing Premium Purchase Feature

This template includes a premium purchase feature that allows users to buy the Premium version of ShipFree. This feature is **completely isolated** from the template's payment system and can be easily removed if you don't need it.

### What is the Premium Purchase Feature?

The premium purchase feature is used to sell the Premium template itself ($90 one-time payment). It includes:
- Stripe Checkout integration for one-time payments
- Success page that collects GitHub email and Twitter handle
- Discord invite link display after purchase
- Client-side purchase tracking via localStorage

**Important:** This is separate from the template's built-in payment system (Stripe, Polar, Lemon Squeezy, Creem) which is used by your application to accept payments from your customers.

### How to Remove

To completely remove the premium purchase feature:

1. **Delete the following folders:**
   - `app/api/premium-purchase/`
   - `app/api/webhooks/premium-purchase/` (if created)
   - `app/[locale]/(site)/premium-purchase/`

2. **Delete the following files:**
   - `lib/premium-purchase.ts`
   - `lib/premium-purchase/hooks.ts`
   - `app/[locale]/(site)/pricing/premium-button.tsx`

3. **Update `app/[locale]/(site)/pricing.tsx`:**
   - Remove the import: `import { PremiumButton } from './pricing/premium-button'`
   - Replace `<PremiumButton />` with a regular button or remove the button handler
   - Look for the comment: `// Premium template purchase - can be removed if not needed`

4. **Remove environment variables from `config/env.ts`:**
   - Remove all variables with `PREMIUM_PURCHASE_` prefix:
     - `PREMIUM_PURCHASE_STRIPE_SECRET_KEY` (server)
     - `PREMIUM_PURCHASE_STRIPE_PRICE_ID` (server)
     - `PREMIUM_PURCHASE_STRIPE_WEBHOOK_SECRET` (server, optional)
     - `NEXT_PUBLIC_PREMIUM_PURCHASE_STRIPE_PUBLISHABLE_KEY` (client)
     - `NEXT_PUBLIC_PREMIUM_PURCHASE_DISCORD_INVITE_LINK` (client)

5. **Remove from `.env` file:**
   - Remove all `PREMIUM_PURCHASE_*` environment variables

6. **Remove webhook endpoint (if configured):**
   - Remove the webhook endpoint from your Stripe Dashboard pointing to `/api/webhooks/premium-purchase`

After removal, the template's payment functionality (for your customers) will continue to work normally. The premium purchase feature is completely decoupled and does not affect any other features.

---

Cooked for you with ❤️ by [Revoks](https://revoks.dev)
