feat(db): mesh data model — meshes, members, invites, audit log

- pgSchema "mesh" with 4 tables isolating the peer mesh domain
- Enums: visibility, transport, tier, role
- audit_log is metadata-only (E2E encryption enforced at broker/client)
- Cascade on mesh delete, soft-delete via archivedAt/revokedAt

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alejandro Gutiérrez
2026-04-04 21:19:32 +01:00
commit d3163a5bff
1384 changed files with 314925 additions and 0 deletions

329
.context/ARCHITECTURE.md Normal file
View File

@@ -0,0 +1,329 @@
# TurboStarter Architecture Reference
> LLM context document. Token-optimized. Source of truth: code in `packages/` and `apps/`.
## Stack Summary
```
Apps: web (Next.js 16, React 19) | mobile (Expo 54, React Native)
API: Hono (packages/api) → /api/* routes
Auth: Better Auth 1.4.6 (packages/auth)
DB: PostgreSQL + Drizzle ORM + pgvector (packages/db)
Billing: Stripe | LemonSqueezy | Polar (packages/billing)
AI: Multi-provider via AI SDK (packages/ai)
UI: shadcn/ui + Radix (packages/ui-web, packages/ui-mobile)
```
## Directory Map
```
apps/
web/src/
app/[locale]/ # Next.js App Router (i18n)
(marketing)/ # Public: landing, blog, pricing
auth/ # Login, register, password reset
dashboard/
(user)/ # Personal dashboard
[organization]/ # Multi-tenant org routes
admin/ # Super admin
modules/ # Feature modules
lib/api/ # API client (server.ts, client.tsx)
config/ # paths.ts, app.ts
mobile/src/
app/ # Expo Router
modules/ # Feature modules
packages/
api/src/
index.ts # Main router, exports AppRouter type
modules/ # Feature routers (admin/, ai/, auth/, billing/, organizations/, storage/)
auth/src/
server.ts # Better Auth config
client.tsx # Client helpers
db/src/
schema/ # Drizzle schemas (auth.ts, chat.ts, image.ts, pdf.ts, customer.ts, credit-transaction.ts)
migrations/ # SQL migration files
ai/src/modules/
chat/ # Multi-provider chat
image/ # Image generation
pdf/ # RAG pipeline
tts/ # Text-to-speech
stt/ # Speech-to-text
credits/ # Usage metering
billing/src/
providers/ # stripe/, lemonsqueezy/, polar/
i18n/translations/ # JSON translation files
ui/web/src/ # 45+ shadcn components
ui/mobile/src/ # React Native components
```
## Database Schema
### Tables by Schema
| Schema | Table | Key Columns | Purpose |
|--------|-------|-------------|---------|
| public | `user` | id, email, emailVerified, banned, role, isAnonymous | Users |
| public | `session` | id, userId, activeOrganizationId, impersonatedBy | Sessions |
| public | `account` | userId, providerId, accountId | OAuth accounts |
| public | `verification` | identifier, value, expiresAt | Email tokens |
| public | `passkey` | userId, publicKey, credentialId | WebAuthn |
| public | `two_factor` | userId, secret, backupCodes | 2FA |
| public | `organization` | id, name, slug | Orgs |
| public | `member` | userId, organizationId, role | Membership |
| public | `invitation` | email, organizationId, role, status | Invites |
| public | `customer` | userId, customerId, plan, credits | Billing |
| public | `credit_transaction` | customerId, type, amount, balance | Credit ledger |
| chat | `chat` | id, userId, title | Chat sessions |
| chat | `message` | chatId, role, content | Messages |
| chat | `part` | messageId, type, order, details | Message parts |
| image | `generation` | userId, prompt, model, aspectRatio | Image requests |
| image | `image` | generationId, url | Generated images |
| pdf | `document` | userId, name, status, s3Key | PDF files |
| pdf | `chat` | documentId, userId | PDF chats |
| pdf | `message` | chatId, role, content | PDF messages |
| pdf | `embedding` | documentId, chunkIndex, embedding(1536) | Vectors |
| pdf | `retrieval_chunk` | documentId, content, pageNumber | Semantic chunks |
| pdf | `citation_unit` | documentId, pageNumber, bbox, content | Citations |
### Enums
```typescript
// packages/db/src/schema/auth.ts
memberRole: 'owner' | 'admin' | 'member'
invitationStatus: 'pending' | 'accepted' | 'rejected' | 'canceled'
// packages/db/src/schema/customer.ts
billingPlan: 'free' | 'premium' | 'enterprise'
// packages/db/src/schema/credit-transaction.ts
creditTransactionType: 'signup' | 'purchase' | 'usage' | 'admin_grant' | 'admin_deduct' | 'refund' | 'promo' | 'referral' | 'expiry'
// packages/db/src/schema/chat.ts
messageRole: 'system' | 'assistant' | 'user'
// packages/db/src/schema/image.ts
aspectRatio: '1:1' | '16:9' | '9:16' | '4:3' | '3:4'
// packages/db/src/schema/pdf.ts
documentStatus: 'pending' | 'processing' | 'ready' | 'failed'
citationUnitType: 'prose' | 'heading' | 'list' | 'table' | 'code'
```
## API Pattern
### Router Structure
```typescript
// packages/api/src/index.ts
app.route("/admin", adminRouter) // Admin operations
app.route("/ai", aiRouter) // AI features
app.route("/auth", authRouter) // Auth (Better Auth)
app.route("/billing", billingRouter)// Billing webhooks/checkout
app.route("/organizations", orgRouter)
app.route("/storage", storageRouter)
```
### Module Pattern
```
packages/api/src/modules/<feature>/
router.ts # Hono router
queries.ts # Read operations
mutations.ts # Write operations
```
### Type-Safe Client
```typescript
// Server component
import { api } from "~/lib/api/server";
const data = await api.admin.users.$get({ query: { page: "1" } });
// Client component
import { api } from "~/lib/api/client";
const { data } = useQuery({ queryKey: ["users"], queryFn: () => api.admin.users.$get() });
```
## AI Providers
### Chat Models
| Provider | Models | Package |
|----------|--------|---------|
| OpenAI | gpt-5.1, gpt-4o, o3, o4-mini | @ai-sdk/openai |
| Anthropic | claude-4-sonnet, claude-3.7-sonnet | @ai-sdk/anthropic |
| Google | gemini-2.5-pro, gemini-2.5-flash | @ai-sdk/google |
| xAI | grok-4, grok-3-mini-fast | @ai-sdk/xai |
| DeepSeek | deepseek-v3, deepseek-r1 | @ai-sdk/deepseek |
### Image Models
| Provider | Models |
|----------|--------|
| OpenAI | gpt-image-1, dall-e-2, dall-e-3 |
| Replicate | recraft-v3, photon, stable-diffusion-3.5 |
### Other AI Services
| Service | Provider | Location |
|---------|----------|----------|
| TTS | ElevenLabs | packages/ai/src/modules/tts/ |
| STT | OpenAI Whisper | packages/ai/src/modules/stt/ |
| Embeddings | OpenAI | packages/ai/src/modules/pdf/ |
| Vector Search | pgvector + HNSW | packages/db/src/schema/pdf.ts |
## Auth Configuration
### Methods
```
Email/Password | Magic Link | OAuth (Google, GitHub, Apple) | Passkeys | 2FA/TOTP | Anonymous
```
### Session Fields
```typescript
session: {
userId: string
activeOrganizationId?: string // Multi-tenant context
impersonatedBy?: string // Admin impersonation
}
```
### RBAC
```
Organization roles: owner > admin > member
User role field: 'user' | 'admin' (super admin)
```
## Billing Configuration
### Providers
```typescript
// packages/billing/src/providers/
stripe/ # Stripe subscriptions + one-time
lemonsqueezy/ # LemonSqueezy
polar/ # Polar
```
### Credit System
```typescript
// Deduct credits
await deductCredits(customerId, amount, 'usage', { feature: 'chat' });
// Check balance
const balance = await getCreditsBalance(customerId);
// Transaction types
'signup' | 'purchase' | 'usage' | 'admin_grant' | 'admin_deduct' | 'refund' | 'promo' | 'referral' | 'expiry'
```
## Commands
```bash
# Dev
pnpm install # Install deps
pnpm services:start # Start PostgreSQL (Docker)
pnpm with-env -F @turbostarter/db db:setup # Migrate + seed
pnpm dev # Start all apps
pnpm --filter web dev # Web only
pnpm --filter mobile ios # Mobile iOS
# Database
pnpm with-env -F @turbostarter/db db:generate # Generate migration
pnpm with-env -F @turbostarter/db db:migrate # Apply migrations
pnpm with-env -F @turbostarter/db db:push # Push (dev only)
pnpm with-env -F @turbostarter/db db:studio # GUI
# Quality
pnpm typecheck # Type check all
pnpm lint # ESLint
pnpm test # Vitest
pnpm build # Build all
```
## Critical Invariants
### Must Do
- Use `pnpm with-env` for all DB commands
- Go through API layer for data access
- Server-side auth/authz enforcement
- Use Drizzle ORM, never raw SQL (except migrations)
- Use existing UI components from packages/ui-*
### Must Not
- Access DB directly from apps
- Client-side auth checks as security
- Business logic in React components
- Skip migrations in production
- Introduce new state management libs
## File Patterns
### Add Dashboard Page
```
1. Define path: apps/web/src/config/paths.ts
2. Add sidebar item: apps/web/src/app/[locale]/dashboard/(user)/layout.tsx
3. Create page: apps/web/src/app/[locale]/dashboard/(user)/my-feature/page.tsx
4. Add translations: packages/i18n/translations/en/dashboard.json
```
### Add API Endpoint
```
1. Create module: packages/api/src/modules/<feature>/
2. Add router.ts, queries.ts, mutations.ts
3. Mount in packages/api/src/index.ts
4. Types auto-available via Hono RPC
```
### Add DB Table
```
1. Edit schema: packages/db/src/schema/<domain>.ts
2. Export from packages/db/src/schema/index.ts
3. Generate: pnpm with-env -F @turbostarter/db db:generate
4. Migrate: pnpm with-env -F @turbostarter/db db:migrate
```
## Package Exports
| Package | Export | Use |
|---------|--------|-----|
| @turbostarter/db | /server | Server-only DB access |
| @turbostarter/auth | /server, /client | Auth helpers |
| @turbostarter/api | /utils | handle(), response helpers |
| @turbostarter/i18n | /server, /client | Translation functions |
| @turbostarter/ui-web | /<component> | UI components |
## Environment Variables
### Required (turbo.json globalEnv)
```
DATABASE_URL # PostgreSQL connection
PRODUCT_NAME # App name
URL # Base URL
DEFAULT_LOCALE # Default language (en)
```
### Location
```
.env # Root (DB, shared secrets)
apps/web/.env.local # Web-specific
apps/mobile/.env.local # Mobile-specific
```
## Not In This Codebase
- Browser extension (apps/extension) - available in TurboStarter Core separately
- WXT framework references
- Extension-specific docs in .context/turbostarter-framework-context/sections/extension/

View File

@@ -0,0 +1,41 @@
# TurboStarter Framework Context
TurboStarter framework documentation for AI context loading.
## When to Read More
**Read `index.md`** if you need to:
- Find TurboStarter documentation on a specific topic
- Search by keyword (auth, database, billing, api, etc.)
- Understand what documentation is available
**Read `framework.md`** for:
- pnpm commands and workflows
- Monorepo structure
- Code conventions
## Quick Reference
| Need | Read |
|------|------|
| Commands & patterns | `framework.md` |
| Authentication | `sections/web/auth/` |
| Database/Drizzle | `sections/web/database/` |
| API/Hono | `sections/web/api/` |
| Billing/Stripe | `sections/web/billing/` |
| UI Components | `sections/web/ui/` |
| Organizations | `sections/web/organizations/` |
| i18n | `sections/web/i18n/` |
| Mobile | `sections/mobile/` |
## Refreshing
```bash
python .context/turbostarter-framework-context/refresh-docs.py
```
## Notes
- These docs are **subordinate** to `.context/CLAUDE.md`
- Adapt patterns to match existing codebase, don't copy verbatim
- When in doubt, check the actual code in `packages/` and `apps/`

View File

@@ -0,0 +1,992 @@
# TurboStarter Framework Patterns
TurboStarter framework patterns and commands.
> **Note:** This file is **subordinate to `.context/CLAUDE.md`**. Project-specific decisions take precedence.
>
> **For more docs:** Check `index.md` for keyword search across 222 documentation pages.
## Purpose
This document contains TurboStarter monorepo patterns, commands, and architecture guidelines:
- Framework-specific commands (pnpm, database, services)
- Monorepo structure and package organization
- Code conventions and patterns established by TurboStarter
**When to consult `.context/project.md` instead:**
- Project-specific architecture decisions
- Project package structure
- Business logic and feature requirements
**Documentation usage**: Examples here are illustrative. Adapt them to match existing repository patterns rather than copying verbatim.
## Project Overview
This is a TurboStarter monorepo - a fullstack SaaS starter kit built with Turborepo. It contains:
- **Web app**: Next.js 16 (App Router) with React 19
- **Mobile app**: React Native + Expo
- **Shared packages**: API (Hono), auth (Better Auth), database (Drizzle + PostgreSQL), billing, email, i18n, UI components
## Essential Commands
### Development
```bash
# Install dependencies (uses pnpm 10.25.0)
pnpm install
# Start Docker services (PostgreSQL)
pnpm services:start
# First-time database setup (migrate + seed)
pnpm with-env -F @turbostarter/db db:setup
# Start all apps in dev mode
pnpm dev
# Start specific app only
pnpm --filter web dev
pnpm --filter mobile dev
# Mobile-specific dev commands
pnpm --filter mobile ios # Run on iOS simulator
pnpm --filter mobile android # Run on Android emulator
```
### Database Operations
All database commands must use `pnpm with-env` to load environment variables from `.env` at repo root.
```bash
# Generate migration after schema changes
pnpm with-env -F @turbostarter/db db:generate
# Apply migrations
pnpm with-env -F @turbostarter/db db:migrate
# Push schema directly (dev only - skips migrations)
pnpm with-env -F @turbostarter/db db:push
# Check schema drift
pnpm with-env -F @turbostarter/db db:check
# Open Drizzle Studio (database GUI)
pnpm with-env -F @turbostarter/db db:studio
# Check migration status
pnpm with-env -F @turbostarter/db db:status
# Seed database (dev only)
pnpm with-env -F @turbostarter/db db:seed
# Reset database (dev only)
pnpm with-env -F @turbostarter/db db:reset
```
### Quality & Testing
```bash
# Type check entire monorepo
pnpm typecheck
# Lint (check)
pnpm lint
# Lint (fix)
pnpm lint:fix
# Format (check)
pnpm format
# Format (fix)
pnpm format:fix
# Run tests (Vitest)
pnpm test
# Run tests in watch mode
pnpm test:projects:watch
# Build all packages/apps
pnpm build
# Build specific app
pnpm --filter web build
```
### Services
```bash
# Start Docker services
pnpm services:start
# Stop Docker services
pnpm services:stop
# View service logs
pnpm services:logs
# Check service status
pnpm services:status
```
## Architecture
### Monorepo Structure
- `apps/web/` - Next.js web application
- `apps/mobile/` - React Native (Expo) mobile app
- `packages/api/` - Hono API server with modular routers
- `packages/auth/` - Better Auth configuration and helpers
- `packages/billing/` - Billing integrations (Stripe, LemonSqueezy)
- `packages/db/` - Drizzle ORM schema, migrations, and database utilities
- `packages/email/` - Email templates and providers
- `packages/i18n/` - Internationalization setup and translations
- `packages/shared/` - Common utilities, hooks, constants
- `packages/storage/` - File storage providers and types
- `packages/ui/` - Shared UI components (web/mobile variants)
### API Architecture (Hono)
The API is built with Hono and follows a modular router pattern:
**Main router** (`packages/api/src/index.ts`):
- Base path: `/api`
- Applies middleware: CSRF (web only), CORS, logger, localization
- Routes to sub-routers: `/admin`, `/ai`, `/auth`, `/billing`, `/organizations`, `/storage`
**Module pattern**:
Each feature module (e.g., `packages/api/src/modules/admin/users/`) contains:
- `router.ts` - Hono router with route definitions
- `queries.ts` - Database query functions
- `mutations.ts` - Database mutation functions
- Schema validation via Zod
**Type safety**:
- API types are exported from `packages/api/src/index.ts` as `AppRouter`
- Consumed in web/mobile apps via Hono RPC client for end-to-end type safety
### Web App Structure (Next.js App Router)
```
apps/web/src/app/
├── [locale]/ # Internationalized routes
│ ├── (marketing)/ # Public routes (landing, blog, pricing)
│ ├── auth/ # Auth pages (login, register, password reset)
│ ├── dashboard/ # Protected routes
│ │ ├── (user)/ # Personal dashboard
│ │ └── [organization]/ # Organization-scoped routes
│ └── admin/ # Super admin dashboard
└── api/[...route]/route.ts # Catch-all API route (proxies to Hono)
```
**Route groups**:
- `(marketing)` - Public pages, shared marketing layout
- `(user)` - Personal user dashboard
- `[organization]` - Multi-tenant org routes, slug-based
**API integration**:
- Server components: Use `api` from `~/lib/api/server.ts`
- Client components: Use `api` from `~/lib/api/client.tsx` with React Query
### Database (Drizzle + PostgreSQL)
**Schema location**: `packages/db/src/schema/`
- Multiple schema files organized by domain
- Exported via `packages/db/src/schema/index.ts`
**Migrations**:
- Generated in `packages/db/migrations/` as SQL files
- Workflow: Edit schema → `db:generate``db:migrate`
**Database client**:
- Server-side only via `@turbostarter/db/server`
- Uses Drizzle ORM with PostgreSQL driver
- Connection pooling configured for serverless
**Critical invariants**:
-**Never** access database directly from web/mobile apps
-**Never** use raw SQL outside migrations (use Drizzle queries)
-**Never** skip migrations in production (only `db:push` for local dev)
-**Always** use `pnpm with-env` for all database commands
-**Always** go through API layer for data access
### Authentication (Better Auth)
- Server config: `packages/auth/src/server.ts`
- Client helpers: `packages/auth/src/client.tsx`
- Supports: email/password, magic links, OAuth providers, 2FA, passkeys
- Session management via cookies
- Organizations plugin enabled for multi-tenancy
### Multi-tenancy / Organizations
- Organization-scoped routes: `/dashboard/[organization]/...`
- Active organization stored in session (`activeOrganizationId`)
- RBAC: owner, admin, member roles
- Invitation system with email-based invites
### Business Logic Placement
**Core principle**: Business logic lives in the API layer, not in UI components.
**Where logic belongs**:
- **API layer** (`packages/api/src/modules/`): Business rules, validation, authorization, data transformations
- **Database layer** (`packages/db`): Schema definitions, relations, type-safe queries via Drizzle
- **Web/Mobile apps**: Orchestration, presentation, user interaction, calling API endpoints
- **UI packages**: Pure presentation components, no business rules
**Where logic must NOT live**:
- React components (web or mobile)
- UI packages (`@turbostarter/ui-web`, `@turbostarter/ui-mobile`)
- Directly in API route files (use `queries.ts`/`mutations.ts` instead)
- Client-side validation as source of truth (use for UX only; validate server-side)
**Example**:
```tsx
// ❌ BAD - business logic in component
export function UserProfile({ userId }) {
const canEdit = user.role === 'admin' || user.id === userId;
// Complex business rules in component
}
// ✅ GOOD - business logic in API
export function UserProfile({ userId }) {
const { data } = useQuery(api.users.canEdit.$get({ query: { userId } }));
// API returns authorization decision
}
```
### Layout & Sidebar Patterns
**Dashboard Layout Hierarchy**:
```
layout.tsx (root)
└── [locale]/layout.tsx (i18n wrapper)
└── dashboard/layout.tsx (main dashboard - no sidebar)
├── (user)/layout.tsx (user sidebar + auth check)
│ ├── page.tsx (home)
│ ├── ai/page.tsx
│ └── settings/layout.tsx (sub-nav)
│ ├── page.tsx (general)
│ ├── security/page.tsx
│ └── billing/page.tsx
├── [organization]/layout.tsx (org sidebar + auth check + org fetch)
│ ├── page.tsx (org home)
│ ├── members/page.tsx
│ └── settings/layout.tsx (sub-nav)
└── admin/layout.tsx (admin sidebar + permission check)
├── page.tsx (admin home)
├── users/page.tsx
└── organizations/page.tsx
```
**Sidebar Structure**:
Each layout defines its own sidebar menu with groups:
- **User sidebar**: Personal features (home, AI) + account (settings)
- **Organization sidebar**: Platform features (home) + organization (settings, members)
- **Admin sidebar**: Admin resources (users, organizations, customers)
**Common sidebar footer** (all sidebars):
- Support link
- Feedback link
- User navigation (profile, logout)
**Layout Authentication Patterns**:
```tsx
// User dashboard - basic auth
const { user } = await getSession();
if (!user) return redirect(pathsConfig.auth.login);
// Organization dashboard - auth + org fetch + hydration
const { user } = await getSession();
if (!user) return redirect(pathsConfig.auth.login);
const org = await getOrganization({ slug });
if (!org) return redirect(pathsConfig.dashboard.user.index);
// Pre-fetch and hydrate organization data via queryClient
// Admin dashboard - auth + permission check
const { user } = await getSession();
if (!user) return redirect(pathsConfig.auth.login);
if (!hasAdminPermission(user)) return redirect(pathsConfig.dashboard.user.index);
```
## Environment Variables
**Required globals** (defined in `turbo.json`):
- `DATABASE_URL` - PostgreSQL connection string
- `PRODUCT_NAME` - Application name
- `URL` - Base URL for web app
- `DEFAULT_LOCALE` - Default language (e.g., "en")
**Setup**:
1. Create `.env` at repo root
2. Copy from `.env.example` files
3. Commands automatically load via `pnpm with-env`
**App-specific variables**:
- Web: `apps/web/.env.local`
- Mobile: `apps/mobile/.env.local`
## Common UI Patterns
### Dashboard Components
Standard dashboard components from `~/modules/common/layout/dashboard/`:
- `DashboardHeader` - Page header container
- `DashboardHeaderTitle` - Main page title (h1)
- `DashboardHeaderDescription` - Subtitle/description text
- `DashboardInset` - Main content wrapper with proper spacing
- `DashboardSidebar` - Collapsible sidebar with menu
- `SidebarLink` - Navigation link with active state
### Data Tables
For admin/list pages, use the data table pattern:
- `DataTableSkeleton` - Loading skeleton during Suspense
- `createSearchParamsCache` from `nuqs/server` - Type-safe URL params
- `handle()` from `@turbostarter/api/utils` - Unwraps API responses
- React Query for client-side data fetching
- Built-in sorting, filtering, pagination via URL params
### Icons
Import from `@turbostarter/ui-web/icons`:
```tsx
import { Icons } from "@turbostarter/ui-web/icons";
<Icons.Home />
<Icons.Settings />
<Icons.UsersRound />
```
Common icons:
- `Home`, `Settings`, `UsersRound`, `Building` (sidebar)
- `Brain` (AI), `HandCoins` (billing), `LifeBuoy` (support)
- `MessageCircle` (feedback)
### Internationalization (i18n)
- Translation keys: `namespace:key.nested.path`
- Server: `const { t } = await getTranslation({ ns: "dashboard" })`
- Client: `const { t } = useTranslation({ ns: "dashboard" })`
- Sidebar labels auto-translate if key exists in `common.json`
- Metadata: `getMetadata({ title: "common:myFeature" })`
### UI Components (shadcn/ui)
TurboStarter uses [shadcn/ui](https://ui.shadcn.com) for atomic, accessible, customizable components built with Tailwind CSS and Radix UI.
**Two UI packages**:
- `@turbostarter/ui` - Shared styles, themes, assets (icons)
- `@turbostarter/ui-web` - Pre-built web components (Button, Card, Dialog, etc.)
**Adding new components**:
```bash
# From repo root - launches interactive CLI
pnpm --filter @turbostarter/ui-web ui:add
# Or copy-paste from shadcn/ui website into packages/ui/web/src/
```
**Using components** (each has standalone export):
```tsx
// Import from specific component path
import { Card, CardContent, CardHeader } from "@turbostarter/ui-web/card";
import { Button } from "@turbostarter/ui-web/button";
import { Dialog, DialogContent } from "@turbostarter/ui-web/dialog";
// Build app-specific components by composition
export function MyComponent() {
return (
<Card>
<CardHeader>...</CardHeader>
<CardContent>...</CardContent>
</Card>
);
}
```
**Component organization principle**:
- **Shared package** (`@turbostarter/ui-web`): Atomic components (Button, Input, Card, Dialog)
- **App directory** (`apps/web/src/`): Specific composed components (LoginForm, UserProfile)
- Keep shared components atomic for reusability and tree-shaking
## Security Boundaries
**Critical principle**: Authentication and authorization are **server-side only**. Client-side checks are for UX, never security.
**Security rules**:
-**Auth/authz enforcement**: API layer only (via Better Auth + middleware)
-**Secrets/env vars**: Server-side packages only (`packages/api`, `packages/db`)
-**Role checks**: API layer, never client components
-**Organization permissions**: Validated in API, pre-fetched for UX
**Never do this**:
- ❌ Client-side role/permission checks as source of truth
- ❌ Secrets in web/mobile apps or UI packages
- ❌ Authorization logic in React components
- ❌ Direct API calls bypassing authentication
**Pattern**:
```tsx
// ❌ BAD - client-side auth check
export function AdminPanel() {
if (user.role !== 'admin') return null; // Security by obscurity!
return <SensitiveData />;
}
// ✅ GOOD - server-side enforcement
export async function AdminPanel() {
const { user } = await getSession();
if (!hasAdminPermission(user)) redirect(pathsConfig.dashboard.user.index);
const data = await api.admin.getSensitiveData(); // API enforces auth
return <SensitiveData data={data} />;
}
```
## Architectural Constraints
**Allowed patterns** (use these freely):
- Hono for API routing
- Drizzle ORM for database queries
- Zod for validation
- Better Auth for authentication
- React Server Components (default)
- `nuqs` for URL state
- shadcn/ui for UI components
**Forbidden patterns** (do not introduce):
- ❌ New state management libraries (Redux, Zustand, MobX)
- ❌ Database access outside `@turbostarter/db/server`
- ❌ Bypassing API layer from apps (direct DB access)
- ❌ Business logic in React components
- ❌ Client-side auth checks as security boundaries
- ❌ Ad-hoc environment variable loading (use existing patterns)
- ❌ New packages without justification (see "Adding a new package")
- ❌ Runtime schema mutations or direct SQL queries
**When in doubt**: Ask the user before introducing new dependencies, patterns, or architectural changes.
## Reuse-First Principle
**Critical rule**: Always search for and reuse existing implementations before creating new ones.
### Before implementing ANY feature, check:
1. **Existing UI components** (`packages/ui/web/src/`, `packages/ui/mobile/src/`):
- Check for similar components (forms, buttons, modals, tables)
- Use shadcn/ui components via `pnpm --filter @turbostarter/ui-web ui:add`
- Don't recreate what already exists in the UI packages
2. **Existing utilities** (`packages/shared/src/`):
- Common functions, hooks, constants
- Check `packages/shared/src/utils/` before writing helpers
- Check `packages/shared/src/hooks/` before creating custom hooks
3. **Existing API patterns** (`packages/api/src/modules/`):
- Look at similar endpoints (users, organizations, admin)
- Reuse query/mutation patterns from existing modules
- Use established error handling and validation patterns
4. **Existing database queries** (`packages/db/src/`):
- Check for similar queries in other modules
- Reuse Drizzle query patterns
- Don't duplicate relationship definitions
5. **Existing patterns in similar pages**:
- Dashboard pages: check `apps/web/src/app/[locale]/dashboard/`
- Admin pages: check `apps/web/src/app/[locale]/admin/`
- Auth pages: check `apps/web/src/app/[locale]/auth/`
- Settings pages: check sub-navigation patterns
### Search workflow before implementing:
```bash
# Search for similar functionality
grep -r "keyword" packages/
grep -r "ComponentName" apps/web/src/
# Check UI components
ls packages/ui/web/src/
ls packages/ui/mobile/src/
# Check utilities
ls packages/shared/src/utils/
ls packages/shared/src/hooks/
```
### Examples of reuse over reimplementation:
**Bad - Reimplementing**:
```tsx
// Creating a new button variant when one exists
export function MyCustomButton() {
return <button className="custom-styles">Click</button>;
}
```
**Good - Reusing**:
```tsx
// Using existing button with variant
import { Button } from "@turbostarter/ui-web/button";
export function MyFeature() {
return <Button variant="outline">Click</Button>;
}
```
**Bad - Duplicating logic**:
```tsx
// Writing custom date formatter
function formatDate(date: Date) {
return date.toLocaleDateString();
}
```
**Good - Using existing utility**:
```tsx
// Check if packages/shared has formatDate first
import { formatDate } from "@turbostarter/shared/utils";
```
**Bad - Creating new API pattern**:
```tsx
// Inventing new error handling
if (!user) throw new Error("Not found");
```
**Good - Following existing patterns**:
```tsx
// Copy pattern from packages/api/src/modules/admin/users/
if (!user) {
return c.json({ error: "User not found" }, 404);
}
```
### Decision tree for new implementations:
```
Does similar functionality exist?
├─ YES → Reuse or extend it
│ └─ Can you extend the existing component/utility?
│ ├─ YES → Add props/options to existing code
│ └─ NO → Compose with existing primitives
└─ NO → Implement new, but:
├─ Follow established patterns from similar code
├─ Use existing primitives (UI components, utilities)
└─ Make it reusable for future needs
```
### What this prevents:
- ❌ Duplicate button/input/modal components
- ❌ Multiple implementations of the same utility function
- ❌ Inconsistent API response formats
- ❌ Different auth/validation patterns across features
- ❌ Reimplementing data table patterns
- ❌ Creating custom hooks that already exist
### Required agent behavior:
Before implementing ANY feature:
1. **Search** the codebase for similar implementations
2. **Read** existing code in the same domain (admin, dashboard, auth)
3. **Ask** the user if you're unsure whether something exists
4. **Reuse** existing components, utilities, and patterns
5. **Only create new** when genuinely needed and nothing similar exists
**Optimization target**: Minimize code duplication and maximize consistency through reuse.
## Code Conventions
### TypeScript
- Write concise, technical TypeScript code
- Prefer functional and declarative patterns over classes
- Use interfaces over type aliases
- Avoid enums; use const objects with `as const` instead
- Descriptive variable names with auxiliary verbs (e.g., `isLoading`, `hasError`)
- Prefer iteration and modularization over code duplication
### React (Web)
- **Favor React Server Components** (default in Next.js App Router)
- Minimize `"use client"` directive - only when necessary for:
- Browser APIs (localStorage, window)
- Event handlers and interactivity
- React hooks (useState, useEffect)
- Keep `use client` scoped to specific components; avoid at layout level
- Minimize client-side state (`useState`, `useEffect`)
- Wrap client components in `Suspense` with fallbacks
- Use dynamic imports for non-critical client components
- Use Tailwind CSS for styling; mobile-first responsive design
- Use Shadcn/Radix UI components from `@turbostarter/ui-web`
- Image optimization: WebP format, responsive sizes, lazy loading
- Focus on Web Vitals: LCP, CLS, FID
- Use `nuqs` for URL search param state management
### React Native (Mobile)
- Use safe area primitives: `SafeAreaProvider`, `SafeAreaView`, scroll variants
- Minimize `useState` and `useEffect`; prefer memoization
- Use `React.memo`, `useMemo`, `useCallback` for performance
- Expo Router for file-based navigation
- Use Expo SplashScreen for loading states
- Optimize images: `expo-image` package, WebP format
- Shared UI components from `@turbostarter/ui-mobile`
### File Organization
1. Exported component/function first
2. Sub-components below
3. Helper functions
4. Static content
5. Types/interfaces at bottom
### Error Handling
- Use guard clauses and early returns
- Expected errors: model as return values in Server Actions
- Unexpected errors: let error boundaries catch
- API: Use Zod for input validation
- Handle edge cases early in function logic
### Imports
- Use path aliases:
- Apps: `~/` maps to `src/`
- Packages: `@turbostarter/<package-name>`
- Group imports: external → internal → types
- Add imports and types explicitly; avoid `any` and unsafe casts
### Code Quality
- Adhere to existing formatting; do not reformat unrelated code
- Match existing code style and patterns
- Keep components small and modular
- When modifying multiple areas, prefer creating shared helpers in `packages/` to avoid duplication
### AI Agent Guidelines
When working autonomously, prioritize:
**What to do**:
- ✅ Prefer modifying existing patterns over creating new ones
- ✅ Keep changes minimal and scoped to the specific task
- ✅ Preserve existing functionality unless explicitly asked to change it
- ✅ Follow the established patterns in similar files
- ✅ Ask for clarification when requirements conflict with this document
- ✅ Use TypeScript's type system to catch errors early
**What to avoid**:
- ❌ Large refactors without explicit instruction
- ❌ Introducing breaking changes to APIs or schemas silently
- ❌ "Improving" code that isn't part of the task
- ❌ Making assumptions when requirements are ambiguous
- ❌ Adding dependencies without justification
**Optimize for**: Maintainability, type safety, and consistency with existing architecture over "clever" solutions.
## Key Workflows
### Adding a new API endpoint
1. Create module in `packages/api/src/modules/<feature>/`
2. Define router with Hono, add queries/mutations
3. Export router from module
4. Mount in `packages/api/src/index.ts`
5. Client auto-gets types via Hono RPC
### Database schema changes
1. Edit schema in `packages/db/src/schema/`
2. Generate migration: `pnpm with-env -F @turbostarter/db db:generate`
3. Review generated SQL in `packages/db/migrations/`
4. Apply: `pnpm with-env -F @turbostarter/db db:migrate`
5. Verify in Studio: `pnpm with-env -F @turbostarter/db db:studio`
### Adding a new dashboard page
**1. Define path in `apps/web/src/config/paths.ts`**:
```ts
// For user dashboard
dashboard: {
user: {
myFeature: `${DASHBOARD_PREFIX}/my-feature`,
}
}
// For organization dashboard
organization: (slug: string) => ({
myFeature: `${DASHBOARD_PREFIX}/${slug}/my-feature`,
})
// For admin dashboard
admin: {
myResource: {
index: `${ADMIN_PREFIX}/my-resource`,
detail: (id: string) => `${ADMIN_PREFIX}/my-resource/${id}`,
}
}
```
**2. Add sidebar menu item in layout**:
- User dashboard: `apps/web/src/app/[locale]/dashboard/(user)/layout.tsx`
- Organization: `apps/web/src/app/[locale]/dashboard/[organization]/layout.tsx`
- Admin: `apps/web/src/app/[locale]/admin/layout.tsx`
```ts
const menu = [
{
label: "platform", // or "account", "organization", "admin"
items: [
{
title: "myFeature", // i18n key from common.json
href: pathsConfig.dashboard.user.myFeature,
icon: Icons.YourIcon, // from @turbostarter/ui-web/icons
},
],
},
];
```
**3. Create page file** with standard structure:
**Basic page** (`page.tsx`):
```tsx
import { getTranslation } from "@turbostarter/i18n/server";
import { getMetadata } from "~/lib/metadata";
import {
DashboardHeader,
DashboardHeaderTitle,
DashboardHeaderDescription,
} from "~/modules/common/layout/dashboard/header";
export const generateMetadata = getMetadata({
title: "common:myFeature",
description: "dashboard:myFeature.description",
});
export default async function MyFeaturePage() {
const { t } = await getTranslation({ ns: "dashboard" });
return (
<>
<DashboardHeader>
<div>
<DashboardHeaderTitle>{t("myFeature.title")}</DashboardHeaderTitle>
<DashboardHeaderDescription>
{t("myFeature.description")}
</DashboardHeaderDescription>
</div>
</DashboardHeader>
{/* Page content */}
</>
);
}
```
**Data table page** (admin/list pages):
```tsx
import { createSearchParamsCache, parseAsInteger } from "nuqs/server";
import { Suspense } from "react";
import { handle } from "@turbostarter/api/utils";
import { DataTableSkeleton } from "@turbostarter/ui-web/data-table/data-table-skeleton";
import { api } from "~/lib/api/server";
const searchParamsCache = createSearchParamsCache({
page: parseAsInteger.withDefault(1),
perPage: parseAsInteger.withDefault(10),
sort: getSortingStateParser().withDefault([{ id: "name", desc: false }]),
q: parseAsString,
});
export default async function MyResourcesPage(props: {
searchParams: Promise<Record<string, string | string[] | undefined>>;
}) {
const searchParams = await props.searchParams;
const { page, perPage, sort, ...filters } = searchParamsCache.parse(searchParams);
const promise = handle(api.admin.myResources.$get)({
query: { page: page.toString(), perPage: perPage.toString(), sort: JSON.stringify(sort) },
});
return (
<>
<DashboardHeader>
<DashboardHeaderTitle>My Resources</DashboardHeaderTitle>
</DashboardHeader>
<Suspense fallback={<DataTableSkeleton columnCount={5} />}>
<MyResourcesDataTable promise={promise} perPage={perPage} />
</Suspense>
</>
);
}
```
**4. Add sub-navigation** (for settings-like pages):
Create `layout.tsx` with `SettingsNav` pattern:
```tsx
const LINKS = [
{ label: "general", href: pathsConfig.myFeature.general },
{ label: "advanced", href: pathsConfig.myFeature.advanced },
] as const;
export default async function MyFeatureLayout({ children }) {
const { t } = await getTranslation();
return (
<>
<DashboardHeader>
<DashboardHeaderTitle>{t("myFeature.title")}</DashboardHeaderTitle>
<div className="lg:hidden">
<SettingsNav links={LINKS.map(link => ({ ...link, label: t(link.label) }))} />
</div>
</DashboardHeader>
<div className="flex w-full gap-3">
<div className="hidden w-96 lg:block">
<div className="sticky top-[calc(var(--banner-height)+theme(spacing.6))]">
<SettingsNav links={LINKS.map(link => ({ ...link, label: t(link.label) }))} />
</div>
</div>
<div className="flex w-full flex-col gap-6">{children}</div>
</div>
</>
);
}
```
**5. Add translations**:
- Common labels: `packages/i18n/translations/en/common.json`
- Page content: `packages/i18n/translations/en/dashboard.json` or `admin.json`
```json
{
"myFeature": "My Feature",
"myFeature.title": "Feature Title",
"myFeature.description": "Feature description"
}
```
### Adding a new package to monorepo
**When to add a new package** (advanced):
- Only when functionality needs to be shared across multiple apps
- NOT for adding pages/components to a single app (use `apps/web/src/` instead)
- NOT for modifying existing packages
**Steps**:
1. **Generate package**:
```bash
turbo gen package
# Enter package name (e.g., "example" → @turbostarter/example)
```
2. **Enable fast refresh** in `apps/web/next.config.ts`:
```ts
const INTERNAL_PACKAGES = [
// ...existing packages
"@turbostarter/example",
];
```
3. **Define exports** in `package.json`:
```json
{
"exports": {
".": "./src/index.ts", // Default export
"./client": "./src/client.ts", // Client-only code
"./server": "./src/server.ts" // Server-only code
}
}
```
**Why separate exports** (client/server pattern):
- Better tree-shaking (avoid bundling server code in client)
- Clear separation of concerns
- Used in existing packages like `@turbostarter/db` (has `/server` export)
**Usage**:
```tsx
// Default export
import { example } from "@turbostarter/example";
// Named exports (better tree-shaking)
import { clientFn } from "@turbostarter/example/client";
import { serverFn } from "@turbostarter/example/server";
```
### Adding a new app to monorepo
**When to add a new app** (very advanced):
- Only when you need multiple web apps sharing the same infrastructure
- Want to keep pulling updates from TurboStarter for the base `apps/web`
- Alternative: Create a separate repository (often simpler)
**Use git subtree workflow**:
1. **Create subtree** from `apps/web` (one-time setup):
```bash
git subtree split --prefix=apps/web --branch web-branch
```
2. **Add new app** using web as template:
```bash
# Example: create apps/ai-chat from apps/web template
git subtree add --prefix=apps/ai-chat origin web-branch --squash
```
3. **Update new app** when pulling TurboStarter updates:
```bash
# Pull latest from TurboStarter
git pull upstream main
# Update web-branch with latest apps/web
git subtree split --prefix=apps/web --branch web-branch
git push origin web-branch
# Pull updates into your new app
git subtree pull --prefix=apps/ai-chat origin web-branch --squash
```
**Why this approach**:
- Keeps new apps in sync with base web app structure
- Allows selective updates (can modify ai-chat independently)
- Maintains ability to pull upstream TurboStarter updates
### Multi-platform development
- Share logic in `packages/` to avoid duplication
- UI components: separate web (`ui-web`) and mobile (`ui-mobile`) packages
- API client works across web and mobile with same types
## Testing
- Test framework: Vitest
- Unit tests: co-located with source (`*.test.ts`)
- Run tests: `pnpm test` (uses Turbo caching)
- Watch mode: `pnpm test:projects:watch`
## Troubleshooting
### Common Issues
**Node/pnpm version mismatch**:
- Ensure Node >= 22.17.0: `node -v`
- Ensure pnpm 10.25.0: `pnpm -v`
**Services not available / connection refused**:
- Ensure Docker is running
- Start services: `pnpm services:start`
- Check logs: `pnpm services:logs`
- Verify status: `pnpm services:status`
**DATABASE_URL or env not loaded**:
- Create `.env` at repo root (not `.env.local`)
- Use `pnpm with-env` prefix for all DB commands
- Check `turbo.json` for required `globalEnv` variables
**Turbo or module resolution issues after refactors**:
- Clear caches: `pnpm clean`
- Reinstall: `pnpm install`
**Migration drift or conflicts**:
- Check status: `pnpm with-env -F @turbostarter/db db:check`
- Re-generate migration: `db:generate`
- Apply: `db:migrate`
## Performance Tips
- **Prefer targeted commands**: Use `pnpm --filter <app-or-package> <cmd>` to minimize work
- **Use `pnpm with-env`** whenever a command depends on environment variables
- **Leverage Turbo caching**: Commands like `build`, `lint`, `test` are cached
- **Web app**: Prefer React Server Components to reduce client bundle
- **Mobile app**: Memoize components and callbacks to prevent unnecessary re-renders
## Important Notes
- **Never commit `.env` files** - use `.env.example` as templates
- **Always use `pnpm with-env`** for database commands
- **Docker must be running** for local development (PostgreSQL)
- **Node.js >= 22.17.0** required
- **pnpm 10.25.0** is the package manager (enforced via `packageManager` field)
- Conventional Commits enforced via commitlint (husky hook)
- Workspace validation runs on `postinstall` via sherif

View File

@@ -0,0 +1,698 @@
# TurboStarter Documentation Index
**Last updated:** 2025-12-21 13:11
**Total pages:** 222
**Source:** https://www.turbostarter.dev/llms.txt
---
## Quick Reference
Use this index to find TurboStarter documentation. Each link includes a description.
### Categories Overview
| Platform | Pages | Key Topics |
|----------|-------|------------|
| **Ai** | 24 | docs |
| **Extension** | 52 | ai, analytics, api, auth, billing (+17 more) |
| **Mobile** | 55 | ai, analytics, api, auth, billing (+17 more) |
| **Web** | 91 | admin, ai, analytics, api, auth (+21 more) |
---
## Ai
### Docs
*24 pages covering docs functionality.*
| Topic | Description |
|-------|-------------|
| [API](sections/ai/docs/api.md) | Overview of the API service in TurboStarter AI, including its architecture, tech... |
| [Agents](sections/ai/docs/agents.md) | Build powerful, autonomous AI agents capable of performing complex tasks within ... |
| [Anthropic](sections/ai/docs/anthropic.md) | Setup Anthropic provider and learn how to use it in the starter kit. |
| [Architecture](sections/ai/docs/architecture.md) | A quick overview of the different parts of the TurboStarter AI. |
| [Authentication](sections/ai/docs/auth.md) | Learn about the authentication flow in TurboStarter AI. |
| [Billing](sections/ai/docs/billing.md) | Discover how to manage billing and payment methods for AI features. |
| [Chat with PDF](sections/ai/docs/pdf.md) | Engage in conversations with your PDF documents using AI to extract insights and... |
| [Chatbot](sections/ai/docs/chat.md) | Build a powerful AI assistant with multiple LLMs, generative UI, web browsing, a... |
| [Database](sections/ai/docs/database.md) | Overview of the database service in TurboStarter AI. |
| [DeepSeek](sections/ai/docs/deepseek.md) | Integrate DeepSeek's powerful AI models into your applications with minimal setu... |
| [Eleven Labs](sections/ai/docs/eleven-labs.md) | Setup ElevenLabs and learn how to integrate its AI audio capabilities into the s... |
| [Get started](sections/ai/docs.md) | An overview of the TurboStarter AI starter kit. |
| [Google AI](sections/ai/docs/google.md) | Setup Google Generative AI provider and learn how to use its models like Gemini ... |
| [Image Generation](sections/ai/docs/image.md) | Learn how to generate images using AI models within the TurboStarter AI demo app... |
| [Internationalization](sections/ai/docs/internationalization.md) | Learn how we manage internationalization in TurboStarter AI. |
| [Meta](sections/ai/docs/meta.md) | Setup Meta's Llama models and learn how to use them in the starter kit via vario... |
| [OpenAI](sections/ai/docs/openai.md) | Setup OpenAI provider and learn how to use it in the starter kit. |
| [Replicate](sections/ai/docs/replicate.md) | Setup Replicate provider and learn how to use it in the starter kit. |
| [Security](sections/ai/docs/security.md) | Learn about the security measures implemented in TurboStarter AI. |
| [Storage](sections/ai/docs/storage.md) | Explore cloud storage services for AI applications. |
| [Tech stack](sections/ai/docs/stack.md) | Learn which tools and libraries power TurboStarter AI. |
| [Text to Speech](sections/ai/docs/tts.md) | Convert text into natural-sounding speech using advanced AI voice synthesis mode... |
| [UI](sections/ai/docs/ui.md) | Learn more about UI components and design system in AI starter kit. |
| [xAI Grok](sections/ai/docs/xai.md) | Setup xAI provider and learn how to use it in the starter kit. |
## Extension
### Ai
| Topic | Description |
|-------|-------------|
| [AI](sections/extension/ai.md) | Leverage AI in your TurboStarter extension. |
### Analytics
| Topic | Description |
|-------|-------------|
| [Configuration](sections/extension/analytics/configuration.md) | Learn how to configure extension analytics in TurboStarter. |
| [Overview](sections/extension/analytics/overview.md) | Get started with extension analytics in TurboStarter. |
| [Tracking events](sections/extension/analytics/tracking.md) | Learn how to track events in your TurboStarter extension. |
### Api
| Topic | Description |
|-------|-------------|
| [Overview](sections/extension/api/overview.md) | Get started with the API. |
| [Using API client](sections/extension/api/client.md) | How to use API client to interact with the API. |
### Auth
| Topic | Description |
|-------|-------------|
| [Overview](sections/extension/auth/overview.md) | Learn how to authenticate users in your extension. |
| [Session](sections/extension/auth/session.md) | Learn how to manage the user session in your extension. |
### Billing
| Topic | Description |
|-------|-------------|
| [Billing](sections/extension/billing.md) | Get started with billing in TurboStarter. |
### Cli
| Topic | Description |
|-------|-------------|
| [CLI](sections/extension/cli.md) | Start your new app project with a single command. |
### Configuration
| Topic | Description |
|-------|-------------|
| [App configuration](sections/extension/configuration/app.md) | Learn how to setup the overall settings of your extension. |
| [Environment variables](sections/extension/configuration/environment-variables.md) | Learn how to configure environment variables. |
| [Manifest](sections/extension/configuration/manifest.md) | Learn how to configure the manifest of your extension. |
### Customization
*4 pages covering customization functionality.*
| Topic | Description |
|-------|-------------|
| [Adding apps](sections/extension/customization/add-app.md) | Learn how to add apps to your Turborepo workspace. |
| [Adding packages](sections/extension/customization/add-package.md) | Learn how to add packages to your Turborepo workspace. |
| [Components](sections/extension/customization/components.md) | Manage and customize your extension components. |
| [Styling](sections/extension/customization/styling.md) | Get started with styling your extension. |
### Database
| Topic | Description |
|-------|-------------|
| [Database](sections/extension/database.md) | Get started with the database. |
### Extras
| Topic | Description |
|-------|-------------|
| [Extras](sections/extension/extras.md) | See what you get together with the code. |
### Faq
| Topic | Description |
|-------|-------------|
| [FAQ](sections/extension/faq.md) | Find answers to common technical questions. |
### Installation
*8 pages covering installation functionality.*
| Topic | Description |
|-------|-------------|
| [Cloning repository](sections/extension/installation/clone.md) | Get the code to your local machine and start developing your extension. |
| [Common commands](sections/extension/installation/commands.md) | Learn about common commands you need to know to work with the extension project. |
| [Conventions](sections/extension/installation/conventions.md) | Some standard conventions used across TurboStarter codebase. |
| [Development](sections/extension/installation/development.md) | Get started with the code and develop your browser extension. |
| [Editor setup](sections/extension/installation/editor-setup.md) | Learn how to set up your editor for the fastest development experience. |
| [Managing dependencies](sections/extension/installation/dependencies.md) | Learn how to manage dependencies in your project. |
| [Project structure](sections/extension/installation/structure.md) | Learn about the project structure and how to navigate it. |
| [Updating codebase](sections/extension/installation/update.md) | Learn how to update your codebase to the latest version. |
### Internationalization
| Topic | Description |
|-------|-------------|
| [Internationalization](sections/extension/internationalization.md) | Learn how to internationalize your extension. |
### Marketing
| Topic | Description |
|-------|-------------|
| [Marketing](sections/extension/marketing.md) | Learn how to market your mobile application. |
### Monitoring
| Topic | Description |
|-------|-------------|
| [Overview](sections/extension/monitoring/overview.md) | Get started with browser extension monitoring in TurboStarter. |
| [PostHog](sections/extension/monitoring/posthog.md) | Learn how to setup PostHog as your browser extension monitoring provider. |
| [Sentry](sections/extension/monitoring/sentry.md) | Learn how to setup Sentry as your browser extension monitoring provider. |
### Organizations
| Topic | Description |
|-------|-------------|
| [Organizations/teams](sections/extension/organizations.md) | Learn how to use organizations/teams/multi-tenancy in TurboStarter extension. |
### Overview
| Topic | Description |
|-------|-------------|
| [Introduction](sections/extension.md) | Get started with TurboStarter extension kit. |
### Publishing
*5 pages covering publishing functionality.*
| Topic | Description |
|-------|-------------|
| [Checklist](sections/extension/publishing/checklist.md) | Let's publish your TurboStarter extension to stores! |
| [Chrome Web Store](sections/extension/publishing/chrome.md) | Publish your extension to Google Chrome Web Store. |
| [Edge Add-ons](sections/extension/publishing/edge.md) | Publish your extension to Microsoft Edge Add-ons. |
| [Firefox Add-ons](sections/extension/publishing/firefox.md) | Publish your extension to Mozilla Firefox Add-ons. |
| [Updates](sections/extension/publishing/updates.md) | Learn how to update your published extension. |
### Recipes
| Topic | Description |
|-------|-------------|
| [Supabase](sections/extension/recipes/supabase.md) | Learn how to set up Supabase as the database (and optional storage) provider for... |
### Stack
| Topic | Description |
|-------|-------------|
| [Tech Stack](sections/extension/stack.md) | A detailed look at the technical details. |
### Structure
*6 pages covering structure functionality.*
| Topic | Description |
|-------|-------------|
| [Background service worker](sections/extension/structure/background.md) | Configure your extension's background service worker. |
| [Content scripts](sections/extension/structure/content-scripts.md) | Learn more about content scripts. |
| [Messaging](sections/extension/structure/messaging.md) | Communicate between your extension's components. |
| [Overview](sections/extension/structure/overview.md) | Learn about the structure of the extension app. |
| [Pages](sections/extension/structure/pages.md) | Get started with your extension's pages. |
| [Storage](sections/extension/structure/storage.md) | Learn how to store data in your extension. |
### Tests
| Topic | Description |
|-------|-------------|
| [E2E tests](sections/extension/tests/e2e.md) | Simulate real user scenarios across the entire stack with automated end-to-end t... |
| [Unit tests](sections/extension/tests/unit.md) | Write and run fast unit tests for individual functions and components with insta... |
### Troubleshooting
| Topic | Description |
|-------|-------------|
| [Installation](sections/extension/troubleshooting/installation.md) | Find answers to common extension installation issues. |
| [Publishing](sections/extension/troubleshooting/publishing.md) | Find answers to common publishing issues. |
## Mobile
### Ai
| Topic | Description |
|-------|-------------|
| [AI](sections/mobile/ai.md) | Learn how to use AI integration in your mobile app. |
### Analytics
| Topic | Description |
|-------|-------------|
| [Configuration](sections/mobile/analytics/configuration.md) | Learn how to configure mobile analytics in TurboStarter. |
| [Overview](sections/mobile/analytics/overview.md) | Get started with mobile analytics in TurboStarter. |
| [Tracking events](sections/mobile/analytics/tracking.md) | Learn how to track events in your TurboStarter mobile app. |
### Api
| Topic | Description |
|-------|-------------|
| [Overview](sections/mobile/api/overview.md) | Get started with the API. |
| [Using API client](sections/mobile/api/client.md) | How to use API client to interact with the API. |
### Auth
*7 pages covering auth functionality.*
| Topic | Description |
|-------|-------------|
| [Apple](sections/mobile/auth/oauth/apple.md) | Configure "Sign in with Apple" for your mobile application. |
| [Configuration](sections/mobile/auth/configuration.md) | Configure authentication for your application. |
| [Google](sections/mobile/auth/oauth/google.md) | Configure "Sign in with Google" for your mobile application. |
| [OAuth](sections/mobile/auth/oauth.md) | Get started with social authentication. |
| [Overview](sections/mobile/auth/overview.md) | Get started with authentication. |
| [Two-Factor Authentication (2FA)](sections/mobile/auth/2fa.md) | Add an extra layer of security with two-factor authentication in your mobile app... |
| [User flow](sections/mobile/auth/flow.md) | Discover the authentication flow in Turbostarter. |
### Billing
| Topic | Description |
|-------|-------------|
| [Billing](sections/mobile/billing.md) | Get started with billing in TurboStarter. |
### Cli
| Topic | Description |
|-------|-------------|
| [CLI](sections/mobile/cli.md) | Start your new project with a single command. |
### Configuration
| Topic | Description |
|-------|-------------|
| [App configuration](sections/mobile/configuration/app.md) | Learn how to setup the overall settings of your app. |
| [Environment variables](sections/mobile/configuration/environment-variables.md) | Learn how to configure environment variables. |
| [Paths configuration](sections/mobile/configuration/paths.md) | Learn how to configure the paths of your app. |
### Customization
*4 pages covering customization functionality.*
| Topic | Description |
|-------|-------------|
| [Adding apps](sections/mobile/customization/add-app.md) | Learn how to add apps to your Turborepo workspace. |
| [Adding packages](sections/mobile/customization/add-package.md) | Learn how to add packages to your Turborepo workspace. |
| [Components](sections/mobile/customization/components.md) | Manage and customize your app components. |
| [Styling](sections/mobile/customization/styling.md) | Get started with styling your app. |
### Database
| Topic | Description |
|-------|-------------|
| [Database](sections/mobile/database.md) | Get started with the database. |
### Extras
| Topic | Description |
|-------|-------------|
| [Extras](sections/mobile/extras.md) | See what you get together with the code. |
### Faq
| Topic | Description |
|-------|-------------|
| [FAQ](sections/mobile/faq.md) | Find answers to common technical questions. |
### Installation
*9 pages covering installation functionality.*
| Topic | Description |
|-------|-------------|
| [Cloning repository](sections/mobile/installation/clone.md) | Get the code to your local machine and start developing your app. |
| [Common commands](sections/mobile/installation/commands.md) | Learn about common commands you need to know to work with the mobile project. |
| [Conventions](sections/mobile/installation/conventions.md) | Some standard conventions used across TurboStarter codebase. |
| [Development](sections/mobile/installation/development.md) | Get started with the code and develop your mobile SaaS. |
| [Editor setup](sections/mobile/installation/editor-setup.md) | Learn how to set up your editor for the fastest development experience. |
| [Firebase project](sections/mobile/installation/firebase.md) | Learn how to set up a Firebase project for your TurboStarter mobile app. |
| [Managing dependencies](sections/mobile/installation/dependencies.md) | Learn how to manage dependencies in your project. |
| [Project structure](sections/mobile/installation/structure.md) | Learn about the project structure and how to navigate it. |
| [Updating codebase](sections/mobile/installation/update.md) | Learn how to update your codebase to the latest version. |
### Internationalization
| Topic | Description |
|-------|-------------|
| [Internationalization](sections/mobile/internationalization.md) | Learn how to internationalize your mobile app. |
### Marketing
| Topic | Description |
|-------|-------------|
| [Marketing](sections/mobile/marketing.md) | Learn how to market your mobile application. |
### Monitoring
| Topic | Description |
|-------|-------------|
| [Overview](sections/mobile/monitoring/overview.md) | Get started with mobile monitoring in TurboStarter. |
| [PostHog](sections/mobile/monitoring/posthog.md) | Learn how to setup PostHog as your mobile monitoring provider. |
| [Sentry](sections/mobile/monitoring/sentry.md) | Learn how to setup Sentry as your mobile monitoring provider. |
### Organizations
*4 pages covering organizations functionality.*
| Topic | Description |
|-------|-------------|
| [Active organization](sections/mobile/organizations/active-organization.md) | Set and switch the current organization context within your application. |
| [Invitations](sections/mobile/organizations/invitations.md) | Send, track, and accept organization invites. |
| [Overview](sections/mobile/organizations/overview.md) | Learn how to use organizations/teams/multi-tenancy in TurboStarter mobile app. |
| [RBAC (Roles & Permissions)](sections/mobile/organizations/rbac.md) | Manage roles, permissions, and access scopes. |
### Overview
| Topic | Description |
|-------|-------------|
| [Introduction](sections/mobile.md) | Get started with TurboStarter mobile kit. |
### Publishing
*4 pages covering publishing functionality.*
| Topic | Description |
|-------|-------------|
| [App Store (iOS)](sections/mobile/publishing/ios.md) | Learn how to publish your mobile app to the Apple App Store. |
| [Checklist](sections/mobile/publishing/checklist.md) | Let's publish your TurboStarter app to stores! |
| [Google Play (Android)](sections/mobile/publishing/android.md) | Learn how to publish your mobile app to the Google Play Store. |
| [Updates](sections/mobile/publishing/updates.md) | Learn how to update your published app. |
### Push Notifications
| Topic | Description |
|-------|-------------|
| [Push notifications](sections/mobile/push-notifications.md) | Engage your users with personalized notifications. |
### Recipes
| Topic | Description |
|-------|-------------|
| [Supabase](sections/mobile/recipes/supabase.md) | Learn how to set up Supabase as the database (and optional storage) provider for... |
### Stack
| Topic | Description |
|-------|-------------|
| [Tech Stack](sections/mobile/stack.md) | A detailed look at the technical details. |
### Tests
| Topic | Description |
|-------|-------------|
| [E2E tests](sections/mobile/tests/e2e.md) | Simulate real user scenarios across the entire stack with automated end-to-end t... |
| [Unit tests](sections/mobile/tests/unit.md) | Write and run fast unit tests for individual functions and components with insta... |
### Troubleshooting
| Topic | Description |
|-------|-------------|
| [Installation](sections/mobile/troubleshooting/installation.md) | Find answers to common mobile installation issues. |
| [Publishing](sections/mobile/troubleshooting/publishing.md) | Find answers to common mobile publishing issues. |
## Web
### Admin
| Topic | Description |
|-------|-------------|
| [Overview](sections/web/admin/overview.md) | Get started with the admin dashboard in TurboStarter. |
| [Super Admin UI](sections/web/admin/ui.md) | Get familiar with the Super Admin dashboard and start managing your application. |
### Ai
| Topic | Description |
|-------|-------------|
| [Configuration](sections/web/ai/configuration.md) | Configure AI integration in your TurboStarter project. |
| [Overview](sections/web/ai/overview.md) | Get started with AI integration in your TurboStarter project. |
### Analytics
| Topic | Description |
|-------|-------------|
| [Configuration](sections/web/analytics/configuration.md) | Learn how to configure web analytics in TurboStarter. |
| [Overview](sections/web/analytics/overview.md) | Get started with web analytics in TurboStarter. |
| [Tracking events](sections/web/analytics/tracking.md) | Learn how to track events in your TurboStarter web app. |
### Api
*6 pages covering api functionality.*
| Topic | Description |
|-------|-------------|
| [Adding new endpoint](sections/web/api/new-endpoint.md) | How to add new endpoint to the API. |
| [Internationalization](sections/web/api/internationalization.md) | Learn how to localize and translate your API. |
| [Mutations](sections/web/api/mutations.md) | Learn how to mutate data on the server. |
| [Overview](sections/web/api/overview.md) | Get started with the API. |
| [Protected routes](sections/web/api/protected-routes.md) | Learn how to protect your API routes. |
| [Using API client](sections/web/api/client.md) | How to use API client to interact with the API. |
### Auth
*5 pages covering auth functionality.*
| Topic | Description |
|-------|-------------|
| [Configuration](sections/web/auth/configuration.md) | Configure authentication for your application. |
| [OAuth](sections/web/auth/oauth.md) | Get started with social authentication. |
| [Overview](sections/web/auth/overview.md) | Get started with authentication. |
| [Two-Factor Authentication (2FA)](sections/web/auth/2fa.md) | Add an extra layer of security with two-factor authentication. |
| [User flow](sections/web/auth/flow.md) | Discover the authentication flow in Turbostarter. |
### Background Tasks
| Topic | Description |
|-------|-------------|
| [Overview](sections/web/background-tasks/overview.md) | Learn about background tasks & cron jobs and how they can power your application... |
| [Upstash QStash](sections/web/background-tasks/qstash.md) | Integrate Upstash QStash with your TurboStarter application for serverless-first... |
| [trigger.dev](sections/web/background-tasks/trigger.md) | Integrate trigger.dev with your TurboStarter application for reliable background... |
### Billing
*7 pages covering billing functionality.*
| Topic | Description |
|-------|-------------|
| [Configuration](sections/web/billing/configuration.md) | Configure billing for your application. |
| [Creem](sections/web/billing/creem.md) | Manage your customers data and subscriptions using Creem. |
| [Lemon Squeezy](sections/web/billing/lemon-squeezy.md) | Manage your customers data and subscriptions using Lemon Squeezy. |
| [Overview](sections/web/billing/overview.md) | Get started with billing in TurboStarter. |
| [Polar](sections/web/billing/polar.md) | Manage your customers data and subscriptions using Polar. |
| [Stripe](sections/web/billing/stripe.md) | Manage your customers data and subscriptions using Stripe. |
| [Webhooks](sections/web/billing/webhooks.md) | Handle webhooks from your billing provider. |
### Cli
| Topic | Description |
|-------|-------------|
| [CLI](sections/web/cli.md) | Start your new project with a single command. |
### Cms
| Topic | Description |
|-------|-------------|
| [Blog](sections/web/cms/blog.md) | Learn how to manage your blog content. |
| [Content Collections](sections/web/cms/content-collections.md) | Get started with Content Collections. |
| [Overview](sections/web/cms/overview.md) | Manage your content in TurboStarter. |
### Configuration
| Topic | Description |
|-------|-------------|
| [App configuration](sections/web/configuration/app.md) | Learn how to setup the overall settings of your app. |
| [Environment variables](sections/web/configuration/environment-variables.md) | Learn how to configure environment variables. |
| [Paths configuration](sections/web/configuration/paths.md) | Learn how to configure the paths of your app. |
### Customization
*4 pages covering customization functionality.*
| Topic | Description |
|-------|-------------|
| [Adding apps](sections/web/customization/add-app.md) | Learn how to add apps to your Turborepo workspace. |
| [Adding packages](sections/web/customization/add-package.md) | Learn how to add packages to your Turborepo workspace. |
| [Components](sections/web/customization/components.md) | Manage and customize your app components. |
| [Styling](sections/web/customization/styling.md) | Get started with styling your app. |
### Database
*4 pages covering database functionality.*
| Topic | Description |
|-------|-------------|
| [Database client](sections/web/database/client.md) | Use database client to interact with the database. |
| [Migrations](sections/web/database/migrations.md) | Migrate your changes to the database. |
| [Overview](sections/web/database/overview.md) | Get started with the database. |
| [Schema](sections/web/database/schema.md) | Learn about the database schema. |
### Deployment
*9 pages covering deployment functionality.*
| Topic | Description |
|-------|-------------|
| [AWS Amplify](sections/web/deployment/amplify.md) | Learn how to deploy your TurboStarter app to AWS Amplify. |
| [Checklist](sections/web/deployment/checklist.md) | Let's deploy your TurboStarter app to production! |
| [Docker](sections/web/deployment/docker.md) | Learn how to containerize your TurboStarter app with Docker. |
| [Fly.io](sections/web/deployment/fly.md) | Learn how to deploy your TurboStarter app to Fly.io. |
| [Netlify](sections/web/deployment/netlify.md) | Learn how to deploy your TurboStarter app to Netlify. |
| [Railway](sections/web/deployment/railway.md) | Learn how to deploy your TurboStarter app to Railway. |
| [Render](sections/web/deployment/render.md) | Learn how to deploy your TurboStarter app to Render. |
| [Standalone API](sections/web/deployment/api.md) | Learn how to deploy your API as a dedicated service. |
| [Vercel](sections/web/deployment/vercel.md) | Learn how to deploy your TurboStarter app to Vercel. |
### Emails
| Topic | Description |
|-------|-------------|
| [Configuration](sections/web/emails/configuration.md) | Learn how to configure your emails in TurboStarter. |
| [Overview](sections/web/emails/overview.md) | Get started with emails in TurboStarter. |
| [Sending emails](sections/web/emails/sending.md) | Learn how to send emails in TurboStarter. |
### Extras
| Topic | Description |
|-------|-------------|
| [Extras](sections/web/extras.md) | See what you get together with the code. |
### Faq
| Topic | Description |
|-------|-------------|
| [FAQ](sections/web/faq.md) | Find answers to common technical questions. |
### Installation
*8 pages covering installation functionality.*
| Topic | Description |
|-------|-------------|
| [Cloning repository](sections/web/installation/clone.md) | Get the code to your local machine and start developing. |
| [Common commands](sections/web/installation/commands.md) | Learn about common commands you need to know to work with the project. |
| [Conventions](sections/web/installation/conventions.md) | Some standard conventions used across TurboStarter codebase. |
| [Development](sections/web/installation/development.md) | Get started with the code and develop your SaaS. |
| [Editor setup](sections/web/installation/editor-setup.md) | Learn how to set up your editor for the fastest development experience. |
| [Managing dependencies](sections/web/installation/dependencies.md) | Learn how to manage dependencies in your project. |
| [Project structure](sections/web/installation/structure.md) | Learn about the project structure and how to navigate it. |
| [Updating codebase](sections/web/installation/update.md) | Learn how to update your codebase to the latest version. |
### Internationalization
| Topic | Description |
|-------|-------------|
| [Configuration](sections/web/internationalization/configuration.md) | Learn how to configure internationalization in TurboStarter. |
| [Overview](sections/web/internationalization/overview.md) | Get started with internationalization in TurboStarter. |
| [Translating app](sections/web/internationalization/translations.md) | Learn how to translate your application to multiple languages. |
### Marketing
| Topic | Description |
|-------|-------------|
| [Legal pages](sections/web/marketing/legal.md) | Learn how to create and update legal pages |
| [Marketing pages](sections/web/marketing/pages.md) | Discover which marketing pages are available out of the box and how to add a new... |
| [SEO](sections/web/marketing/seo.md) | Learn how to optimize your app for search engines. |
### Monitoring
| Topic | Description |
|-------|-------------|
| [Overview](sections/web/monitoring/overview.md) | Get started with web monitoring in TurboStarter. |
| [PostHog](sections/web/monitoring/posthog.md) | Learn how to setup PostHog as your web monitoring provider. |
| [Sentry](sections/web/monitoring/sentry.md) | Learn how to setup Sentry as your web monitoring provider. |
### Organizations
*5 pages covering organizations functionality.*
| Topic | Description |
|-------|-------------|
| [Active organization](sections/web/organizations/active-organization.md) | Set and switch the current organization context within your application. |
| [Data model](sections/web/organizations/data-model.md) | Entities and relationships for organizations and multi-tenancy. |
| [Invitations](sections/web/organizations/invitations.md) | Send, track, and accept organization invites. |
| [Overview](sections/web/organizations/overview.md) | Learn how to use organizations/teams/multi-tenancy in TurboStarter. |
| [RBAC (Roles & Permissions)](sections/web/organizations/rbac.md) | Manage roles, permissions, and access scopes. |
### Overview
| Topic | Description |
|-------|-------------|
| [Introduction](sections/web.md) | Get started with TurboStarter web kit. |
### Recipes
| Topic | Description |
|-------|-------------|
| [Supabase](sections/web/recipes/supabase.md) | Learn how to set up Supabase for your TurboStarter project. |
### Stack
| Topic | Description |
|-------|-------------|
| [Tech Stack](sections/web/stack.md) | A detailed look at the technical details. |
### Storage
| Topic | Description |
|-------|-------------|
| [Configuration](sections/web/storage/configuration.md) | Learn how to configure storage in TurboStarter. |
| [Managing files](sections/web/storage/managing-files.md) | Learn how to manage files in TurboStarter. |
| [Overview](sections/web/storage/overview.md) | Get started with storage in TurboStarter. |
### Tests
| Topic | Description |
|-------|-------------|
| [E2E tests](sections/web/tests/e2e.md) | Simulate real user scenarios across the entire stack with automated end-to-end t... |
| [Unit tests](sections/web/tests/unit.md) | Write and run fast unit tests for individual functions and components with insta... |
### Troubleshooting
*4 pages covering troubleshooting functionality.*
| Topic | Description |
|-------|-------------|
| [Billing](sections/web/troubleshooting/billing.md) | Find answers to common billing issues. |
| [Deployment](sections/web/troubleshooting/deployment.md) | Find answers to common web deployment issues. |
| [Emails](sections/web/troubleshooting/emails.md) | Find answers to common emails issues. |
| [Installation](sections/web/troubleshooting/installation.md) | Find answers to common web installation issues. |
---
## Quick Lookup by Keyword
Common searches and where to find them:
| Keyword | Related Docs |
|---------|--------------|
| `admin` | [Overview](sections/web/admin/overview.md), [Super Admin UI](sections/web/admin/ui.md) |
| `ai` | [AI](sections/extension/ai.md), [Tech Stack](sections/extension/stack.md), [AI](sections/mobile/ai.md) (+32 more) |
| `anthropic` | [Anthropic](sections/ai/docs/anthropic.md) |
| `api` | [Using API client](sections/extension/api/client.md), [Overview](sections/extension/api/overview.md), [Using API client](sections/mobile/api/client.md) (+8 more) |
| `auth` | [Overview](sections/extension/auth/overview.md), [Two-Factor Authentication (2FA)](sections/mobile/auth/2fa.md), [Configuration](sections/mobile/auth/configuration.md) (+9 more) |
| `billing` | [Billing](sections/extension/billing.md), [Billing](sections/mobile/billing.md), [Configuration](sections/web/billing/configuration.md) (+4 more) |
| `chat` | [Chatbot](sections/ai/docs/chat.md), [Chat with PDF](sections/ai/docs/pdf.md) |
| `database` | [Database](sections/extension/database.md), [Supabase](sections/extension/recipes/supabase.md), [Database](sections/mobile/database.md) (+6 more) |
| `deploy` | [AWS Amplify](sections/web/deployment/amplify.md), [Standalone API](sections/web/deployment/api.md), [Checklist](sections/web/deployment/checklist.md) (+6 more) |
| `docker` | [Docker](sections/web/deployment/docker.md) |
| `email` | [Configuration](sections/web/emails/configuration.md), [Overview](sections/web/emails/overview.md), [Sending emails](sections/web/emails/sending.md) (+1 more) |
| `endpoint` | [Adding new endpoint](sections/web/api/new-endpoint.md) |
| `file` | [Managing files](sections/web/storage/managing-files.md) |
| `migration` | [Migrations](sections/web/database/migrations.md) |
| `oauth` | [OAuth](sections/mobile/auth/oauth.md), [OAuth](sections/web/auth/oauth.md) |
| `openai` | [OpenAI](sections/ai/docs/openai.md) |
| `organization` | [Organizations/teams](sections/extension/organizations.md), [Active organization](sections/mobile/organizations/active-organization.md), [Invitations](sections/mobile/organizations/invitations.md) (+5 more) |
| `payment` | [Billing](sections/ai/docs/billing.md) |
| `permission` | [RBAC (Roles & Permissions)](sections/mobile/organizations/rbac.md), [RBAC (Roles & Permissions)](sections/web/organizations/rbac.md) |
| `role` | [RBAC (Roles & Permissions)](sections/mobile/organizations/rbac.md), [RBAC (Roles & Permissions)](sections/web/organizations/rbac.md) |
| `route` | [Protected routes](sections/web/api/protected-routes.md) |
| `session` | [Session](sections/extension/auth/session.md) |
| `storage` | [Supabase](sections/extension/recipes/supabase.md), [Storage](sections/extension/structure/storage.md), [Supabase](sections/mobile/recipes/supabase.md) (+3 more) |
| `stripe` | [Stripe](sections/web/billing/stripe.md) |
| `subscription` | [Creem](sections/web/billing/creem.md), [Lemon Squeezy](sections/web/billing/lemon-squeezy.md), [Polar](sections/web/billing/polar.md) (+1 more) |
| `team` | [Organizations/teams](sections/extension/organizations.md), [Overview](sections/mobile/organizations/overview.md), [Overview](sections/web/organizations/overview.md) |
| `test` | [Editor setup](sections/extension/installation/editor-setup.md), [Updating codebase](sections/extension/installation/update.md), [E2E tests](sections/extension/tests/e2e.md) (+9 more) |
| `user` | [Overview](sections/extension/auth/overview.md), [Session](sections/extension/auth/session.md), [E2E tests](sections/extension/tests/e2e.md) (+5 more) |
| `vercel` | [Vercel](sections/web/deployment/vercel.md) |

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,345 @@
#!/usr/bin/env python3
"""
TurboStarter Documentation Chunker
Downloads llms.txt from TurboStarter and splits it into organized markdown files.
Creates an index.md with navigation and a CLAUDE.md context file.
Usage:
python refresh-docs.py
Or make executable:
chmod +x refresh-docs.py
./refresh-docs.py
"""
import os
import re
import urllib.request
from pathlib import Path
from datetime import datetime
from collections import defaultdict
# Configuration
LLMS_URL = "https://www.turbostarter.dev/llms.txt"
DOCS_DIR = Path(__file__).parent
OUTPUT_DIR = DOCS_DIR / "sections"
def download_llms_txt():
"""Download the latest llms.txt from TurboStarter."""
print(f"Downloading from {LLMS_URL}...")
with urllib.request.urlopen(LLMS_URL) as response:
content = response.read().decode('utf-8')
# Save full file
full_path = DOCS_DIR / "llms-full.txt"
full_path.write_text(content)
print(f"Saved full file to {full_path} ({len(content)} bytes)")
return content
def parse_frontmatter(text):
"""Extract YAML frontmatter from a document section."""
match = re.match(r'^---\s*\n(.*?)\n---\s*\n', text, re.DOTALL)
if not match:
return None, text
frontmatter = {}
for line in match.group(1).strip().split('\n'):
if ':' in line:
key, value = line.split(':', 1)
frontmatter[key.strip()] = value.strip()
content = text[match.end():]
return frontmatter, content
def url_to_path(url):
"""Convert URL path to filesystem path."""
# /docs/web/database -> web/database
path = url.lstrip('/')
if path.startswith('docs/'):
path = path[5:]
return path
def chunk_docs(content):
"""Split llms.txt into individual documents."""
# Split by document separator (--- at start of line followed by url:)
sections = re.split(r'\n(?=---\s*\nurl:)', content)
docs = []
for section in sections:
section = section.strip()
if not section:
continue
frontmatter, body = parse_frontmatter(section)
if frontmatter and 'url' in frontmatter:
docs.append({
'url': frontmatter.get('url', ''),
'title': frontmatter.get('title', 'Untitled'),
'description': frontmatter.get('description', ''),
'content': body.strip(),
'path': url_to_path(frontmatter.get('url', ''))
})
return docs
def organize_by_category(docs):
"""Group documents by their top-level category."""
categories = defaultdict(list)
for doc in docs:
parts = doc['path'].split('/')
if parts:
category = parts[0] # web, mobile, extension, etc.
categories[category].append(doc)
return dict(categories)
def save_docs(docs):
"""Save chunked documents to filesystem."""
# Clean output directory
if OUTPUT_DIR.exists():
import shutil
shutil.rmtree(OUTPUT_DIR)
OUTPUT_DIR.mkdir(parents=True)
# Group by category
categories = organize_by_category(docs)
saved_files = []
for category, category_docs in categories.items():
category_dir = OUTPUT_DIR / category
category_dir.mkdir(parents=True, exist_ok=True)
for doc in category_docs:
# Create subdirectories if needed
path_parts = doc['path'].split('/')
if len(path_parts) > 1:
subdir = category_dir / '/'.join(path_parts[1:-1]) if len(path_parts) > 2 else category_dir
subdir.mkdir(parents=True, exist_ok=True)
filename = path_parts[-1] + '.md'
filepath = subdir / filename
else:
filepath = category_dir / 'index.md'
# Build markdown content
md_content = f"""---
title: {doc['title']}
description: {doc['description']}
url: {doc['url']}
---
# {doc['title']}
{doc['content']}
"""
filepath.write_text(md_content)
saved_files.append({
'filepath': filepath.relative_to(DOCS_DIR),
'title': doc['title'],
'description': doc['description'],
'url': doc['url'],
'category': category
})
print(f"Saved {len(saved_files)} documentation files")
return saved_files, categories
def generate_index(saved_files, categories, docs):
"""Generate index.md with rich contextual navigation."""
lines = [
"# TurboStarter Documentation Index",
"",
f"**Last updated:** {datetime.now().strftime('%Y-%m-%d %H:%M')} ",
f"**Total pages:** {len(docs)} ",
f"**Source:** https://www.turbostarter.dev/llms.txt",
"",
"---",
"",
"## Quick Reference",
"",
"Use this index to find TurboStarter documentation. Each link includes a description.",
"",
]
# Category overview with counts and key topics
lines.append("### Categories Overview")
lines.append("")
lines.append("| Platform | Pages | Key Topics |")
lines.append("|----------|-------|------------|")
for category in sorted(categories.keys()):
count = len(categories[category])
# Extract unique subcategories as key topics
subcats = set()
for doc in categories[category]:
parts = doc['path'].split('/')
if len(parts) > 1:
subcats.add(parts[1])
topics = ', '.join(sorted(subcats)[:5])
if len(subcats) > 5:
topics += f' (+{len(subcats)-5} more)'
lines.append(f"| **{category.title()}** | {count} | {topics} |")
lines.append("")
lines.append("---")
lines.append("")
# Detailed sections with full context
for category in sorted(categories.keys()):
lines.append(f"## {category.title()}")
lines.append("")
# Group by subcategory
subcats = defaultdict(list)
for doc in categories[category]:
parts = doc['path'].split('/')
subcat = parts[1] if len(parts) > 1 else 'overview'
subcats[subcat].append(doc)
for subcat in sorted(subcats.keys()):
subcat_title = subcat.replace('-', ' ').replace('_', ' ').title()
lines.append(f"### {subcat_title}")
lines.append("")
# Add a contextual summary based on descriptions
subcat_docs = subcats[subcat]
if len(subcat_docs) > 3:
lines.append(f"*{len(subcat_docs)} pages covering {subcat_title.lower()} functionality.*")
lines.append("")
# Table format for better scanning
lines.append("| Topic | Description |")
lines.append("|-------|-------------|")
for doc in sorted(subcat_docs, key=lambda d: d['title']):
filepath = f"sections/{doc['path']}.md"
# Truncate long descriptions
desc = doc['description'][:80] + '...' if len(doc['description']) > 80 else doc['description']
lines.append(f"| [{doc['title']}]({filepath}) | {desc} |")
lines.append("")
# Quick lookup section
lines.append("---")
lines.append("")
lines.append("## Quick Lookup by Keyword")
lines.append("")
lines.append("Common searches and where to find them:")
lines.append("")
# Build keyword index from titles and descriptions
keyword_map = defaultdict(list)
keywords_of_interest = [
'auth', 'login', 'oauth', 'session',
'database', 'drizzle', 'postgres', 'migration',
'api', 'hono', 'endpoint', 'route',
'billing', 'stripe', 'payment', 'subscription',
'email', 'smtp', 'template',
'storage', 's3', 'upload', 'file',
'i18n', 'translation', 'locale',
'admin', 'user', 'role', 'permission',
'organization', 'team', 'member',
'ai', 'openai', 'anthropic', 'chat',
'deploy', 'vercel', 'docker',
'test', 'vitest', 'playwright',
]
for doc in docs:
text = f"{doc['title']} {doc['description']}".lower()
for kw in keywords_of_interest:
if kw in text:
keyword_map[kw].append(doc)
# Output keyword table
lines.append("| Keyword | Related Docs |")
lines.append("|---------|--------------|")
for kw in sorted(keyword_map.keys()):
related = keyword_map[kw][:3] # Max 3 per keyword
links = ', '.join([f"[{d['title']}](sections/{d['path']}.md)" for d in related])
if len(keyword_map[kw]) > 3:
links += f" (+{len(keyword_map[kw])-3} more)"
lines.append(f"| `{kw}` | {links} |")
lines.append("")
index_path = DOCS_DIR / "index.md"
index_path.write_text('\n'.join(lines))
print(f"Generated index at {index_path}")
def generate_claude_md():
"""Generate CLAUDE.md context file for the docs folder."""
content = """# TurboStarter Framework Context
TurboStarter framework documentation for AI context loading.
## When to Read More
**Read `index.md`** if you need to:
- Find TurboStarter documentation on a specific topic
- Search by keyword (auth, database, billing, api, etc.)
- Understand what documentation is available
**Read `framework.md`** for:
- pnpm commands and workflows
- Monorepo structure
- Code conventions
## Quick Reference
| Need | Read |
|------|------|
| Commands & patterns | `framework.md` |
| Authentication | `sections/web/auth/` |
| Database/Drizzle | `sections/web/database/` |
| API/Hono | `sections/web/api/` |
| Billing/Stripe | `sections/web/billing/` |
| UI Components | `sections/web/ui/` |
| Organizations | `sections/web/organizations/` |
| i18n | `sections/web/i18n/` |
| Mobile | `sections/mobile/` |
## Refreshing
```bash
python .context/turbostarter-framework-context/refresh-docs.py
```
## Notes
- These docs are **subordinate** to `.context/CLAUDE.md`
- Adapt patterns to match existing codebase, don't copy verbatim
- When in doubt, check the actual code in `packages/` and `apps/`
"""
claude_path = DOCS_DIR / "CLAUDE.md"
claude_path.write_text(content)
print(f"Generated CLAUDE.md at {claude_path}")
def main():
print("=" * 60)
print("TurboStarter Documentation Chunker")
print("=" * 60)
print()
# Download latest docs
content = download_llms_txt()
# Parse and chunk
print("Parsing documentation sections...")
docs = chunk_docs(content)
print(f"Found {len(docs)} documentation pages")
# Save to filesystem
print("Saving chunked files...")
saved_files, categories = save_docs(docs)
# Generate navigation files
print("Generating navigation files...")
generate_index(saved_files, categories, docs)
generate_claude_md()
print()
print("=" * 60)
print("Done! Documentation is ready in .context/turbostarter-framework-context/")
print("=" * 60)
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,173 @@
---
title: Get started
description: An overview of the TurboStarter AI starter kit.
url: /ai/docs
---
# Get started
TurboStarter AI is a **starter kit with ready-to-use demo apps** that helps you quickly build powerful AI applications without starting from scratch. Whether you're launching a small side project or a full-scale enterprise solution, it provides the structure you need to jump right into building your own unique AI application.
<AppsShowcase className="pt-2 [&_a]:no-underline [&_img]:my-0" />
## Features
TurboStarter AI comes packed with features designed to accelerate your development process:
### Core framework
<Cards>
<Card title="Monorepo setup" description="Powered by Turborepo for efficient code sharing and dependency management across web and mobile applications." className="shadow-none" />
<Card title="Next.js web app" description="Leverages Next.js for server-side rendering, static site generation, and API routes." className="shadow-none" />
<Card title="Hono API" description="Ultra-fast API framework optimized for edge computing with comprehensive TypeScript support." className="shadow-none" />
<Card title="React Native + Expo" description="Foundation for cross-platform mobile apps that share business logic with your web application." className="shadow-none" />
</Cards>
### AI
<Cards>
<Card title="Vercel AI SDK" description="Complete toolkit for implementing advanced AI features like streaming responses and interactive chat interfaces." className="shadow-none" />
<Card title="LangChain" description="Powerful framework for building sophisticated AI applications with prompt management, memory systems, and agent capabilities." className="shadow-none" />
<Card title="Multiple AI providers" description="Seamless integration with OpenAI, Anthropic, Google AI, xAI, Meta, Deepseek, Replicate, Eleven Labs, and more through a unified interface." className="shadow-none" />
<Card title="Specialized models" description="Full support for text generation, structured output, image creation, voice synthesis, and embedding models." className="shadow-none" />
<Card title="One-line model switching" description="Effortlessly switch between AI models or providers with minimal code changes." className="shadow-none" />
</Cards>
### Data storage
<Cards>
<Card title="Drizzle ORM" description="Type-safe ORM for efficient interaction with PostgreSQL (default) or other supported databases (MySQL, SQLite)." className="shadow-none" />
<Card title="PostgreSQL database" description="Reliable storage for chat history, user data, and vector embeddings with optimized performance." className="shadow-none" />
<Card title="Vector embeddings" description="Built-in support for storing and retrieving vector embeddings for advanced retrieval-augmented generation." className="shadow-none" />
<Card title="Blob storage" description="Integrated S3-compatible storage for managing user uploads, AI-generated content, and documents." className="shadow-none" />
</Cards>
### Authentication
<Cards>
<Card title="Better Auth integration" description="Secure authentication system starting with anonymous sessions, extensible to email/password, magic links, and OAuth providers." className="shadow-none" />
<Card title="Rate limiting" description="Intelligent protection for API endpoints against abuse and overuse." className="shadow-none" />
<Card title="Credits-based access" description="Flexible system to manage and control AI feature usage with customizable credit allocation." className="shadow-none" />
<Card title="Backend API key management" description="Security-first approach ensuring sensitive API keys remain protected on the server side." className="shadow-none" />
</Cards>
### User interface
<Cards>
<Card title="Tailwind CSS & shadcn/ui" description="Utility-first CSS framework and pre-designed components for rapid UI development." className="shadow-none" />
<Card title="Radix UI" description="Accessible, unstyled components that provide the foundation for beautiful, functional interfaces." className="shadow-none" />
<Card title="Shared UI package" description="Centralized UI component library ensuring consistency across all applications in the monorepo." className="shadow-none" />
</Cards>
## Demo apps
TurboStarter AI includes several production-ready demo applications that showcase diverse AI capabilities. Use these examples to understand implementation patterns and jumpstart your own projects.
<Cards>
<Card title="Chatbot" description="Build intelligent conversational experiences with an AI chatbot featuring contextual reasoning and real-time web search capabilities." href="/ai/docs/chat" icon={<Chatting01Icon />} />
<Card title="Image generation" description="Create compelling visuals with a versatile AI image generator supporting multiple models, styles, and output resolutions." href="/ai/docs/image" icon={<Image02Icon />} />
<Card title="Chat with PDF" description="Extract valuable insights from documents by having natural conversations with your PDFs using context-aware AI." href="/ai/docs/pdf" icon={<File01Icon />} />
<Card title="Text to speech" description="Convert written content into lifelike speech with support for over 5,000+ voices across multiple languages and styles." href="/ai/docs/tts" icon={<AudioWaves />} />
<Card title="Agents" description="Develop autonomous AI agents capable of executing complex tasks by orchestrating multiple AI models and tools." href="/ai/docs/agents" icon={<WorkflowCircle01Icon />} />
</Cards>
## Scope of this documentation
This documentation focuses specifically on the AI features, architecture, and demo applications included in the **TurboStarter AI** kit. While we provide comprehensive coverage of AI integrations, for information about core framework elements (authentication, billing, etc.), please refer to the [Core documentation](/docs/web).
Our goal is to guide you through setting up, customizing, and deploying the AI starter kit efficiently. Where relevant, we include links to official documentation for the integrated AI providers and libraries.
## Setup
Getting started with TurboStarter AI requires configuring the core applications first. For detailed setup instructions, refer to:
<Cards>
<Card title="Web app setup" description="Follow our step-by-step guide in the Core web documentation to set up your web application." href="/docs/web/installation/development" icon={<Website />} />
<Card title="Mobile app setup" description="Use our detailed guide in the Core mobile documentation to configure your mobile application." href="/docs/mobile/installation/development" icon={<Phone />} />
</Cards>
After establishing the core applications, you can configure specific AI providers and demo applications using the dedicated sections in this documentation (see sidebar). For a quick start, you might also want to check our [TurboStarter CLI guide](/blog/the-only-turbo-cli-you-need-to-start-your-next-project-in-seconds) to bootstrap your project in seconds.
<Callout>
When working with the AI starter kit, remember to use the `ai` repository instead of `core` for Git commands. For example, use `git clone turbostarter/ai` rather than `git clone turbostarter/core`.
</Callout>
## Deployment
Deploying TurboStarter AI follows the same process as deploying the core web application. Ensure you configure all necessary environment variables, including those for your selected AI providers (like [OpenAI](/ai/docs/openai), [Anthropic](/ai/docs/anthropic), etc.), in your deployment environment.
For comprehensive deployment instructions across various platforms, consult our core deployment guides:
<Cards>
<Card title="Deployment checklist" description="General checklist before deploying the web app." href="/docs/web/deployment/checklist" />
<Card title="Vercel" description="Streamlined deployment process for Vercel hosting." href="/docs/web/deployment/vercel" />
<Card title="Railway" description="Step-by-step guide for deploying to Railway." href="/docs/web/deployment/railway" />
<Card title="Docker" description="Container-based deployment using Docker." href="/docs/web/deployment/docker" />
<Card title="Other Providers" description="Additional guides for Netlify, Render, AWS Amplify, Fly.io and more." href="/docs/web/deployment/checklist" />
</Cards>
For mobile app store deployment, refer to our mobile publishing guides:
<Cards>
<Card title="Publishing checklist" description="Comprehensive pre-publishing verification for mobile applications." href="/docs/mobile/publishing/checklist" />
<Card title="Updates" description="Best practices for managing updates to published mobile apps." href="/docs/mobile/publishing/updates" />
</Cards>
Each AI demo app may have specific deployment considerations, so check their dedicated documentation sections for additional guidance.
## `llms.txt`
Access the complete TurboStarter documentation in Markdown format at [/llms.txt](/llms.txt). This file contains all documentation in an LLM-friendly format, enabling you to ask questions about TurboStarter using the most current information.
### Example usage
To query an LLM about TurboStarter:
1. Copy the documentation contents from [/llms.txt](/llms.txt)
2. Use this prompt format with your preferred LLM:
```
Documentation:
{paste documentation here}
---
Based on the above documentation, answer the following:
{your question}
```
## Let's build amazing AI!
We're excited to help you create innovative AI-powered applications quickly and efficiently. If you have questions, encounter issues, or want to showcase your creations, connect with our community:
* [Follow updates on X](https://x.com/turbostarter_)
* [Join our Discord](https://discord.gg/KjpK2uk3JP)
* [Report issues on GitHub](https://github.com/turbostarter)
* [Contact us via email](mailto:hello@turbostarter.dev)
Happy building! 🚀

View File

@@ -0,0 +1,56 @@
---
title: Agents
description: Build powerful, autonomous AI agents capable of performing complex tasks within your web and mobile applications.
url: /ai/docs/agents
---
# Agents
<Callout title="Agents are coming soon!">
This feature is currently under development and will be
available in a future release.
[See roadmap](https://github.com/orgs/turbostarter/projects/1)
</Callout>
The AI Agents demo will showcase how to create intelligent, autonomous agents capable of executing complex tasks within your web and mobile applications.
These agents will leverage advanced AI techniques to interact with users, tools, and data sources.
## Features
<Cards>
<Card title="Cross-platform">
Design agents once and deploy them seamlessly across multiple platforms
including React, React Native, Expo, and Next.js through a unified
architecture.
</Card>
<Card title="Memory">
Implement sophisticated context retention that allows agents to maintain
state and recall critical information across conversations and devices with
perfect continuity.
</Card>
<Card title="Function calling">
Enable agents to take meaningful actions by integrating with external tools,
accessing APIs, and executing functions dynamically within secure,
controlled environments.
</Card>
<Card title="MCP integration">
Leverage the [Model Context
Protocol](https://modelcontextprotocol.io/introduction) to standardize
context delivery between agents and Large Language Models (LLMs). This
enables frictionless connections to diverse data sources and tools,
dramatically enhancing agent capabilities.
</Card>
<Card title="Agentic workflows">
Orchestrate complex workflows combining Retrieval-Augmented Generation
(RAG), tool utilization, and MCP server interactions to solve sophisticated
tasks that previously required human intervention.
</Card>
</Cards>
Stay tuned for the release of this exciting functionality!

View File

@@ -0,0 +1,104 @@
---
title: Anthropic
description: Setup Anthropic provider and learn how to use it in the starter kit.
url: /ai/docs/anthropic
---
# Anthropic
The [Anthropic](https://www.anthropic.com) provider integrates Anthropic's powerful Claude models into your application through the AI SDK, with an emphasis on safety, helpfulness, and natural interactions.
![Anthropic](/images/docs/ai/providers/anthropic.png)
## Setup
<Steps>
<Step>
### Generate API Key
Visit the [Anthropic Console](https://console.anthropic.com/) to create an account and generate a new API key for your project.
</Step>
<Step>
### Add API Key to Environment
Add your generated API key to your project's `.env` file (e.g., in `apps/web`):
```bash title=".env"
ANTHROPIC_API_KEY=your-api-key
```
</Step>
<Step>
### Configure Provider (Optional)
The starter kit automatically uses the `ANTHROPIC_API_KEY` environment variable. For advanced configurations (such as proxies or custom headers), refer to the [AI SDK Anthropic documentation](https://sdk.vercel.ai/providers/ai-sdk-providers/anthropic#provider-instance).
</Step>
</Steps>
## Features
<Cards>
<Card title="Language Models" href="https://sdk.vercel.ai/providers/ai-sdk-providers/anthropic#language-models">
Leverage Anthropic's state-of-the-art Claude models for sophisticated
conversational AI, creative text generation, in-depth analysis, and more
through the intuitive Messages API.
</Card>
<Card title="Image Input / Vision" href="https://sdk.vercel.ai/providers/ai-sdk-providers/anthropic#image-input">
Enable models to understand and process image inputs alongside text for
multimodal applications.
</Card>
<Card title="Tool Usage / Function Calling" href="https://sdk.vercel.ai/providers/ai-sdk-providers/anthropic#tool-usage">
Allow models to interact with external tools and APIs to perform actions and
retrieve real-time information.
</Card>
<Card title="Object Generation" href="https://sdk.vercel.ai/providers/ai-sdk-providers/anthropic#object-generation">
Create structured data outputs (like JSON) from natural language prompts,
streamlining the integration of AI capabilities with your existing systems.
</Card>
<Card title="Reasoning" href="https://sdk.vercel.ai/providers/ai-sdk-providers/anthropic#reasoning">
Access detailed insights into the model's thought process, enhancing
transparency, debuggability, and trust in AI-generated responses.
</Card>
<Card title="Computer Use" href="https://sdk.vercel.ai/providers/ai-sdk-providers/anthropic#computer-use">
(Experimental) Allow models to directly interact with computer desktop
environments to complete complex, multi-step tasks autonomously.
</Card>
</Cards>
## Use Cases
<Cards>
<Card title="AI Chatbot">
Craft intelligent, context-aware chatbots capable of nuanced conversations
and sophisticated task completion. Experience this capability in our [Chat
Demo](/ai/docs/chat).
</Card>
<Card title="Content Generation & Summarization">
Generate high-quality text for various purposes, or summarize long documents
and conversations accurately.
</Card>
<Card title="Data Extraction & Analysis">
Extract structured information from unstructured text or analyze complex
data sets combined with visual inputs for comprehensive insights.
</Card>
<Card title="Automated Workflows">
Seamlessly integrate Claude models with your existing tools via function
calling to automate complex business processes and tasks. Explore
[Agents](/ai/docs/agents) for advanced implementation options.
</Card>
</Cards>
## Links
* [Anthropic Website](https://www.anthropic.com)
* [Anthropic Documentation](https://docs.anthropic.com)
* [AI SDK - Anthropic Provider Docs](https://sdk.vercel.ai/providers/ai-sdk-providers/anthropic)

View File

@@ -0,0 +1,52 @@
---
title: API
description: Overview of the API service in TurboStarter AI, including its architecture, technology stack, and core functionalities.
url: /ai/docs/api
---
# API
The API service acts as the central hub for all backend logic within TurboStarter AI. It handles interactions with AI models, data processing, and communication between the frontend and backend systems.
## Technology
We use [Hono](https://hono.dev), a lightning-fast web framework optimized for edge computing. This ensures efficient handling of API requests—particularly critical for real-time AI interactions like streaming responses.
**Importantly, this single API layer serves both web and mobile applications, guaranteeing consistent business logic and data handling across all platforms.**
## AI integration
While the API package (`@turbostarter/api`) exposes the endpoints, the core AI logic lives in a dedicated package: `@turbostarter/ai`. This package is strictly responsible for:
* Communicating with various AI providers and models ([OpenAI](/ai/docs/openai), [Anthropic](/ai/docs/anthropic), [Google AI](/ai/docs/google), etc.)
* Processing and formatting data specifically for AI interactions
* Parsing responses from AI models
* Handling AI-specific data storage or retrieval when necessary
The `@turbostarter/api` package utilizes `@turbostarter/ai` to perform these AI tasks. The API layer itself focuses on registering Hono routes, applying middlewares (like authentication and validation), and exposing AI functionalities to the frontend applications.
This separation ensures AI-specific logic remains modular and reusable, while the API package stays focused on request handling and routing.
<Callout>
API keys for AI services are managed securely on the backend within these packages, ensuring they never appear client-side.
</Callout>
## Middlewares
Hono middlewares streamline request handling by tackling common tasks before the main logic runs. In TurboStarter AI, they handle:
* **Authentication:** verifying user sessions to protect routes, ensuring only logged-in users access certain features
* **Validation:** using schemas to check if incoming request data (like query parameters or JSON bodies) matches expected formats, preventing invalid data from reaching route handlers
* **Rate limiting:** shielding the API from abuse by restricting the number of requests a user or IP address can make within a given timeframe
* **Credits management:** automatically checking if a user has enough credits for an AI operation and deducting the cost before proceeding
* **Localization:** detecting the user's preferred language to deliver localized responses and error messages
These middlewares keep core route logic clean and focused, while consistently enforcing security, usage limits, and data integrity across the API.
## Core API documentation
For general information about the API setup, architecture, authentication integration, and how to add new endpoints, please refer to the [Core API documentation](/docs/web/api/overview).
<Card title="API documentation" href="/docs/web/api/overview" description="Learn about the general API setup, structure, and best practices in the core TurboStarter documentation." />
Specific configurations related to AI providers or demo apps can be found in their respective documentation sections.

View File

@@ -0,0 +1,121 @@
---
title: Architecture
description: A quick overview of the different parts of the TurboStarter AI.
url: /ai/docs/architecture
---
# Architecture
TurboStarter AI integrates several best-in-class open source libraries to power its diverse functionalities, including authentication, data persistence, text generation, and more. Here's a concise overview of the architecture that makes everything work together.
<ThemedImage alt="AI Architecture diagram" light="/images/docs/ai/architecture/light.png" dark="/images/docs/ai/architecture/dark.png" width={2526} height={1561} zoomable />
## Application framework
The project leverages a [monorepo structure](https://turbo.build/repo) powered by [Turborepo](https://turbo.build/) to enable efficient code sharing and consistent tooling across the entire application ecosystem. This approach creates a single source of truth for shared code and dramatically simplifies dependency management.
<Files>
<Folder name="apps" defaultOpen>
<Folder name="web - Web app (Next.js)" />
<Folder name="mobile - Mobile app (React Native - Expo)" />
</Folder>
<Folder name="packages" defaultOpen>
<Folder name="ai - AI features" />
<Folder name="api - API server (including all features logic)" />
<Folder name="auth - Authentication setup" />
<Folder name="db - Database setup" />
<Folder name="i18n - Internationalization setup" />
<Folder name="shared - Shared utilities and helpers" />
<Folder name="storage - Storage setup" />
<Folder name="ui - Atomic UI components">
<Folder name="shared" />
<Folder name="web" />
<Folder name="mobile" />
</Folder>
</Folder>
</Files>
### Web
Built with [Next.js](https://nextjs.org) and [React](https://react.dev), the web application leverages server-side rendering and static site generation for optimal performance and SEO. The UI is styled with [Tailwind CSS](https://tailwindcss.com) and [shadcn/ui](https://ui.shadcn.com) components for rapid development and consistent design. API routes are handled by [Hono](https://hono.dev) for edge computing, chosen for its minimal overhead and excellent TypeScript support.
<Card title="Web | TurboStarter" href="/docs/web" description="Learn more about the core web application and its features." />
### Mobile
The mobile application uses [React Native](https://reactnative.dev) with [Expo](https://expo.dev) for cross-platform development. This combination was selected for its ability to share up to 90% of code between platforms while maintaining native performance. The integration with the monorepo allows seamless sharing of business logic and types with the web application.
<Card title="Mobile | TurboStarter" href="/docs/mobile" description="Learn more about the mobile application and its features." />
## API
The API is implemented as a dedicated package using [Hono](https://hono.dev), a lightweight framework optimized for edge computing. This architectural decision creates a clear separation between frontend and backend logic, enhancing maintainability and testability.
Hono's exceptional TypeScript support ensures type safety across all endpoints, while its minimal footprint and edge-first design deliver outstanding performance.
<Card title="API" href="/ai/docs/api" description="Discover API service in AI starter and demo apps." />
## Model providers
TurboStarter AI seamlessly integrates with leading AI model providers including [OpenAI](/ai/docs/openai), [Anthropic](/ai/docs/anthropic), [Google AI](/ai/docs/google), [xAI](/ai/docs/xai), and more. The architecture employs [AI SDK](https://sdk.vercel.ai/) to create a unified interface across diverse providers, simplifying experimentation with different models.
The platform strategically utilizes specialized models for distinct AI tasks:
* **Text generation** models for conversational AI and content creation
* **Structured output** models for precise data extraction and formatting
* **Image generation** models for visual content creation
* **Voice synthesis** models for natural audio production
* **Embedding** models for semantic search and information retrieval
Switching models requires just a **one-line code change**, allowing you to rapidly adapt to emerging models or change providers based on your specific requirements. This flexibility ensures your application can leverage the latest AI advancements without extensive refactoring.
## Authentication
The applications use [Better Auth](https://www.better-auth.com/) for authentication, providing a secure and flexible authentication system. By default, the AI implementation creates an anonymous user session at startup, which is then used for all subsequent queries and interactions with the AI models. This approach maintains user context across sessions while minimizing friction.
For more sophisticated authentication requirements, you can easily extend the flow by leveraging the [Core implementation](/docs/web/auth/overview), which supports email/password authentication, magic links, OAuth providers, and more. This modular design lets you implement precisely the level of security your application demands.
<Card title="Authentication" href="/ai/docs/auth" description="Learn more about the authentication system in TurboStarter AI." />
## Persistence
Persistence in TurboStarter AI refers to the system's ability to store and retrieve data from a database. The application uses [PostgreSQL](https://www.postgresql.org/) as its primary database to store critical information such as:
* Chat history and conversation context
* User accounts and preference settings
* Vector embeddings for retrieval-augmented generation
To interact with the database from route handlers and server actions, TurboStarter AI leverages [Drizzle ORM](https://orm.drizzle.team/), a high-performance TypeScript ORM that provides type-safe database operations. This ensures robust data integrity and simplified query construction throughout the application.
A key advantage of Drizzle is its compatibility with multiple database providers including [Neon](https://neon.tech/), [Supabase](https://supabase.com/), and [PlanetScale](https://planetscale.com/). This flexibility allows seamless switching between providers based on your specific requirements without modifying queries or schema definitions — making your application highly adaptable to evolving infrastructure needs.
<Card title="Database" href="/ai/docs/database" description="Explore the database architecture and persistence layer in TurboStarter AI." />
## Blob storage
File storage is managed through S3-compatible services, providing scalable, reliable storage for diverse file types. The system efficiently handles user-uploaded images, AI-generated content, and document files. This approach ensures optimal file management and straightforward integration with various storage providers including [AWS S3](https://aws.amazon.com/s3/), [Cloudflare R2](https://www.cloudflare.com/products/r2/), or [MinIO](https://min.io/).
<Card title="Storage" href="/ai/docs/storage" description="Learn more about the storage system in TurboStarter AI." />
## Security
Security is implemented comprehensively to protect both the application and its users. All API endpoints incorporate **rate limiting** to prevent abuse and ensure fair resource allocation.
The system uses a **credits-based access** control system, where each user has a limited number of credits for AI operations, preventing resource exhaustion and enabling monetization options.
All external API interactions, including those with AI model providers, occur exclusively server-side. This ensures that sensitive API keys are **never exposed** to client-side code, significantly reducing vulnerability to unauthorized access or credential theft.
Additionally, the system implements industry-standard security practices including thorough input validation, proper authentication enforcement, and regular dependency security audits.
<Card title="Security" href="/ai/docs/security" description="Explore the security measures in place for TurboStarter AI." />

View File

@@ -0,0 +1,41 @@
---
title: Authentication
description: Learn about the authentication flow in TurboStarter AI.
url: /ai/docs/auth
---
# Authentication
TurboStarter AI implements a streamlined authentication approach powered by [Better Auth](https://www.better-auth.com/). Since the primary focus is showcasing AI capabilities, we've kept the initial authentication simple, allowing you to quickly integrate and experiment with AI features.
## Anonymous sessions
When someone first visits the AI application, an **anonymous session** is automatically created. This establishes a unique user identity without requiring login credentials.
These anonymous sessions serve two critical purposes:
1. **Persistence:** links data like chat history or generated content to specific users in your database
2. **Usage control:** enables tracking for rate limiting and the credits system, ensuring fair AI resource usage even for anonymous visitors
## Extending authentication
While the default anonymous setup provides a frictionless initial experience, TurboStarter is built for growth. The authentication logic uses Better Auth in the shared `packages/auth` package, ensuring consistency between web and mobile applications.
When your project needs more sophisticated authentication features like:
* Email/Password login
* Magic links
* Social logins (OAuth)
* Multi-factor authentication
You can easily integrate these by leveraging the comprehensive authentication system in the [TurboStarter Core kit](/docs/web). The underlying structure is already in place, making this transition straightforward.
For detailed implementation guides, check out the core documentation:
<Cards>
<Card title="Web authentication" href="/docs/web/auth/overview" description="Explore the full authentication capabilities for the web application." />
<Card title="Mobile authentication" href="/docs/mobile/auth/overview" description="Learn how authentication works within the mobile application." />
</Cards>
By starting with anonymous sessions, the AI kit lets you focus on building compelling AI features first, while providing a clear path to implement advanced user management and security as your application evolves.

View File

@@ -0,0 +1,42 @@
---
title: Billing
description: Discover how to manage billing and payment methods for AI features.
url: /ai/docs/billing
---
# Billing
TurboStarter AI includes a straightforward middleware setup to manage user credits for AI features. This lets you control access based on available credits without complex payment integrations.
## Credit-based access
A focused middleware verifies if users have enough credits before allowing them to access specific AI-powered routes or actions.
```ts title="ai.router.ts"
export const aiRouter = new Hono().post(
"/chat",
rateLimiter,
validate("json", chatMessageSchema),
deductCredits({
amount: 10, // [!code highlight]
}),
streamChat,
);
```
This example shows how the `deductCredits` middleware subtracts a specific amount (10 credits) for each request to the `/chat` endpoint.
## Coming soon
We're actively expanding the billing capabilities for AI services, including:
* **Usage-based billing:** implementing a system where users pay based on their actual consumption of AI resources (tokens used, API calls made, etc.)
* **Payment provider integration:** connecting with popular services like [Stripe](/docs/web/billing/stripe), [Lemon Squeezy](/docs/web/billing/lemon-squeezy), and more for hassle-free payment processing
## Extending billing
For more advanced billing scenarios or immediate needs, you can tap into the core TurboStarter billing features. The main documentation provides detailed guidance on setting up and managing billing with third-party providers.
<Card href="/docs/web/billing/overview" title="Billing documentation" description="Learn more about the comprehensive billing features in TurboStarter." />
Stay tuned for updates as we enhance the AI-specific billing functionalities!

View File

@@ -0,0 +1,168 @@
---
title: Chatbot
description: Build a powerful AI assistant with multiple LLMs, generative UI, web browsing, and image analysis.
url: /ai/docs/chat
---
# Chatbot
The [Chatbot](https://ai.turbostarter.dev/chat) demo application showcases an advanced AI assistant capable of engaging in complex conversations, performing web searches, and understanding context. It integrates multiple large language models (LLMs) and allows users to attach files to the chat window.
<AIAppShowcase id="chat" />
## Features
The chatbot offers a variety of capabilities for an enhanced conversational experience:
<Cards>
<Card title="Multi-model integration">
Switch effortlessly between leading AI providers like
[OpenAI](/ai/docs/openai) and [Anthropic](/ai/docs/anthropic) within a
single, consistent chat interface.
</Card>
<Card title="Deep reasoning">
Experience an AI that truly understands complex questions and delivers
thoughtful, nuanced responses based on comprehensive reasoning.
</Card>
<Card title="Live web information">
Access up-to-the-minute information directly from the web through the
integrated search capability powered by [Tavily AI](https://tavily.com/).
</Card>
<Card title="File sharing">
Enrich conversations by sharing and analyzing files, images, or web links
directly within the chat interface for contextual discussion.
</Card>
<Card title="Instant response delivery">
Enjoy natural, fluid conversations with responses that stream in real-time,
eliminating waiting periods.
</Card>
<Card title="Conversation history">
Seamlessly manage your conversation history with features to save, organize,
and revisit previous discussions.
</Card>
</Cards>
## Setup
To implement your advanced AI assistant, you'll need several services configured. If you haven't set these up yet, start with:
<Cards>
<Card href="/ai/docs/database" title="Database" description="Configure a PostgreSQL database to store conversation history and metadata." />
<Card href="/ai/docs/storage" title="Storage" description="Set up S3-compatible storage for handling file attachments." />
</Cards>
### AI models
<Callout>
Different models offer varying capabilities for tool calling, reasoning, and file processing. Consider these differences when selecting the optimal model for your specific use case.
</Callout>
The Chatbot leverages the AI SDK to support various language and vision models. You can easily switch between models based on your needs. Explore the documentation for the most popular models:
<Cards className="grid-cols-1 sm:grid-cols-2">
<Card href="/ai/docs/openai" title="OpenAI" description="Implement GPT and o-series models for powerful text generation." icon={<OpenAI />} />
<Card href="/ai/docs/anthropic" title="Anthropic" description="Integrate Claude models renowned for nuanced reasoning." icon={<Anthropic />} />
<Card href="/ai/docs/google" title="Google AI" description="Incorporate Gemini models for versatile AI capabilities." icon={<Google />} />
<Card href="/ai/docs/xai" title="xAI Grok" description="Leverage xAI's innovative Grok models for advanced interactions." icon={<XAI />} />
</Cards>
For detailed configuration of specific providers and other supported models, refer to the [AI SDK documentation](https://sdk.vercel.ai/providers/ai-sdk-providers).
### Web browsing
The chatbot utilizes [Tavily AI](https://tavily.com/) to provide real-time web search capabilities. Tavily is a specialized search engine optimized for LLMs and AI agents, designed to deliver highly relevant search results by automatically handling the complexities of web scraping, filtering, and extracting relevant information.
We selected Tavily because it dramatically simplifies the integration of current web data into AI applications through a single API call that returns comprehensive, AI-ready search results.
<Callout title="Free tier available">
Tavily offers a generous free tier with [1,000 API credits per
month](https://docs.tavily.com/documentation/api-credits) without requiring
credit card information. A basic search consumes 1 credit, while an advanced
search uses 2 credits. Paid plans are available for higher volume usage.
</Callout>
To enable web browsing, follow these steps:
<Steps>
<Step>
#### Get Tavily API Key
Sign up or log in at the [Tavily Platform](https://app.tavily.com/sign-in) to obtain your API key from the dashboard.
</Step>
<Step>
#### Add API Key to Environment
Add your API key to your project's `.env` file (e.g., in `apps/web`):
```bash title=".env"
TAVILY_API_KEY=tvly-your-api-key
```
</Step>
</Steps>
With the API key properly configured, the chatbot will automatically utilize Tavily for searches when contextually appropriate.
## Data persistence
User interactions and chat history are persisted to ensure a continuous experience across sessions.
<Card href="/ai/docs/database" title="Database" description="Learn more about database service in TurboStarter AI." />
Conversation data is organized within a dedicated PostgreSQL schema named `chat`
to maintain clear separation from other application data.
* `chats`: stores records for each conversation session, including essential metadata like user ID and creation timestamp.
* `messages`: maintains the content of individual messages exchanged within conversations, linked to their parent chat session.
* `parts`: handles complex message structures by breaking down content into smaller components, particularly useful for generative UI elements or multi-modal content.
<Card href="/ai/docs/storage" title="Storage" description="Learn more about cloud storage service in TurboStarter AI." />
Files shared within conversations (such as images or documents) are uploaded to [cloud storage](/ai/docs/storage) (S3-compatible), with references to these attachments stored within the message content or parts.
## Structure
The Chatbot functionality is thoughtfully distributed across shared packages and platform-specific code for web and mobile, ensuring optimal code reuse and consistency.
### Core
The `@turbostarter/ai` package, located in `packages/ai`, contains the central chat functionality in the `src/chat` directory. It includes:
* Essential constants, types, and validation schemas for chat interactions
* Core API logic for managing conversations and messages
* Comprehensive chat history persistence and retrieval functionality
* AI model provider configuration and initialization
* Integrations for external tools like web search
### API
Built with Hono, the `packages/api` package defines all API endpoints. Chat-specific routes are organized under `src/modules/ai/chat`:
* `chat.router.ts`: establishes Hono RPC routes, handles input validation, and connects frontend requests to the core AI logic in `packages/ai`
* Manages authentication, request processing, and database interactions through the core package
### Web
The Next.js web application in `apps/web` implements the user-facing chat interface:
* `src/app/[locale]/(apps)/chat/**`: contains the Next.js App Router pages and layouts dedicated to the chat experience
* `src/components/chat/**`: houses reusable React components for the chat interface (message bubbles, input area, model selector, etc.)
### Mobile
The Expo/React Native mobile application in `apps/mobile` delivers a native chat experience:
* `src/app/chat/**`: defines the primary screens for the mobile chat interface
* `src/components/chat/**`: contains React Native components styled to match the web version, optimized for mobile interaction
* **API interaction**: utilizes the same Hono RPC client (`packages/api`) as the web app for consistent backend communication
This modular structure promotes separation of concerns and facilitates independent development and scaling of different parts of the application.

View File

@@ -0,0 +1,44 @@
---
title: Database
description: Overview of the database service in TurboStarter AI.
url: /ai/docs/database
---
# Database
The database service, managed within the `packages/db` directory (as `@turbostarter/db`), stores data essential for both core application functions and AI features. It ensures that information like user profiles, conversation history, and AI-generated content is reliably preserved and efficiently accessed.
## Technology
We've chosen [PostgreSQL](https://www.postgresql.org) as our primary relational database for its exceptional reliability, extensibility (including powerful tools like `pgvector` for similarity searches), and proven track record in production environments.
Database interactions are handled through [Drizzle ORM](https://orm.drizzle.team/), a cutting-edge TypeScript ORM that offers outstanding type safety (generating types directly from your schema), high performance, and a developer-friendly API.
For detailed guidance on setup, configuration, schema management (including migrations), and general usage patterns of Drizzle and PostgreSQL in the TurboStarter ecosystem, check out our core documentation:
<Cards>
<Card title="Overview" description="Get started with the database in the core web application." href="/docs/web/database/overview" />
<Card title="Schema" description="Learn about the core database schema definitions." href="/docs/web/database/schema" />
<Card title="Migrations" description="Understand how to manage database schema changes over time." href="/docs/web/database/migrations" />
<Card title="Client" description="Learn how to interact with the database using the type-safe Drizzle client." href="/docs/web/database/client" />
</Cards>
## What is stored in the database?
Beyond standard application data (like users and accounts), the database plays a crucial role in storing AI-specific information:
* **Chat history**: saves conversations between users and AI models (including reasoning and usage details), enabling continuous conversations and history features
* **Vector embeddings**: stores numerical representations (vectors) of text data (like document chunks) that power Retrieval-Augmented Generation (RAG) techniques, allowing features like [Chat with PDF](/ai/docs/pdf) to quickly find relevant context from large document collections
* **Document references**: tracks metadata and storage identifiers (paths in [Blob Storage](/ai/docs/storage)) for files like uploaded PDFs or AI-generated images, connecting them to relevant user interactions
* **Tool calls & results**: records actions (such as [web searches](/ai/docs/chat) or calculations) that AI models ([Agents](/ai/docs/agents)) perform, along with their outcomes—valuable for debugging, auditing, and improving agent capabilities
## Schema
The core database schema, defined in `packages/db/src/schema`, contains essential tables for the overall application (users, accounts, sessions, etc.).
To maintain clarity as AI features grow, tables specifically related to AI demo applications (like chat history for the [PDF app](/ai/docs/pdf)) are often placed in dedicated [PostgreSQL schemas](https://www.postgresql.org/docs/current/ddl-schemas.html) (e.g. a schema named `pdf`).
This logical separation helps manage complexity and isolates feature-specific data structures. You'll typically find AI-specific schema definitions either alongside the relevant demo app code or within the main `packages/db/src/schema` directory, clearly labeled and organized.

View File

@@ -0,0 +1,85 @@
---
title: DeepSeek
description: Integrate DeepSeek's powerful AI models into your applications with minimal setup.
url: /ai/docs/deepseek
---
# DeepSeek
The [DeepSeek](https://www.deepseek.com/) provider delivers access to DeepSeek's advanced AI models through the AI SDK, bringing reasoning capabilities to your applications.
![DeepSeek](/images/docs/ai/providers/deepseek.webp)
## Setup
<Steps>
<Step>
### Generate API Key
Visit the [DeepSeek Platform](https://platform.deepseek.com/) and navigate to the API keys section to create your personal secret key.
</Step>
<Step>
### Add API Key to Environment
Add your generated API key to your project's `.env` file (e.g., in `apps/web`):
```bash title=".env"
DEEPSEEK_API_KEY=your-api-key
```
</Step>
<Step>
### Configure Provider (Optional)
The starter kit automatically utilizes the `DEEPSEEK_API_KEY` environment variable. For advanced configurations, consult the comprehensive [AI SDK DeepSeek documentation](https://sdk.vercel.ai/providers/ai-sdk-providers/deepseek#provider-instance).
</Step>
</Steps>
## Features
<Cards>
<Card title="Chat Models" href="https://sdk.vercel.ai/providers/ai-sdk-providers/deepseek#language-models">
Utilize DeepSeek's language models, known for their deep reasoning
capabilities, for tasks like text generation, translation, and
conversational AI applications.
</Card>
<Card title="Reasoning" href="https://platform.deepseek.com/">
Tap into models with reasoning abilities designed specifically for complex
problem-solving, logical deduction, and analytical tasks that require deep
understanding.
</Card>
<Card title="Tool Usage / Function Calling" href="https://sdk.vercel.ai/providers/ai-sdk-providers/deepseek#tool-usage--function-calling">
Enable language models to interact with external tools and functions,
allowing for more complex and automated task execution.
</Card>
</Cards>
## Use Cases
<Cards>
<Card title="AI Chatbot">
Create intelligent chatbots that engage in natural, meaningful conversations
and assist users with a wide range of tasks. Experience this capability in
our [Chat Demo](/ai/docs/chat).
</Card>
<Card title="Content Generation">
Produce diverse, high-quality creative text content including articles,
summaries, code explanations, and marketing copy with language
understanding.
</Card>
<Card title="Automated Workflows">
Integrate language models with other tools via function calling to automate
processes like data analysis or report generation.
</Card>
</Cards>
## Links
* [DeepSeek Website](https://www.deepseek.com/)
* [DeepSeek Platform](https://platform.deepseek.com/)
* [AI SDK - DeepSeek Provider Docs](https://sdk.vercel.ai/providers/ai-sdk-providers/deepseek)

View File

@@ -0,0 +1,143 @@
---
title: Eleven Labs
description: Setup ElevenLabs and learn how to integrate its AI audio capabilities into the starter kit.
url: /ai/docs/eleven-labs
---
# Eleven Labs
[ElevenLabs](https://elevenlabs.io/) stands at the forefront of AI audio innovation, specializing in ultra-realistic Text-to-Speech (TTS), voice cloning, and advanced audio generation. While not a native provider within the AI SDK core, ElevenLabs' powerful services integrate seamlessly with AI applications to deliver exceptional voice experiences.
![ElevenLabs](/images/docs/ai/providers/elevenlabs.jpg)
## Setup
Integrating ElevenLabs involves using their purpose-built SDKs (Python, TypeScript/JavaScript) alongside your application logic:
<Steps>
<Step>
### Generate API Key
Visit the [ElevenLabs website](https://elevenlabs.io/), create an account or sign in, then navigate to your profile settings to generate your unique API key.
</Step>
<Step>
### Add API Key to Environment
Add your API key to your project's `.env` file (e.g., in `apps/web` or the appropriate package):
```bash title=".env"
ELEVENLABS_API_KEY=your-api-key
```
</Step>
<Step>
### Configure SDK
Initialize the ElevenLabs client with your API key:
```typescript title="client.ts"
import { ElevenLabsClient } from "elevenlabs";
import { env } from "../../env";
export const client = new ElevenLabsClient({
apiKey: env.ELEVENLABS_API_KEY,
});
// Now use the client object...
```
For comprehensive implementation details, refer to the [ElevenLabs Quickstart Guide](https://elevenlabs.io/docs/quickstart).
</Step>
</Steps>
## Features
ElevenLabs offers a comprehensive suite of AI audio technologies:
<Cards>
<Card title="Text to Speech (TTS)" href="https://elevenlabs.io/docs/capabilities/text-to-speech">
Transform written text into remarkably natural speech across numerous
languages, voices, and styles, with flexible options for quality or
low-latency delivery.
</Card>
<Card title="Speech to Text (STT)" href="https://elevenlabs.io/docs/capabilities/speech-to-text">
Transcribe spoken audio into text accurately, supporting multiple languages
and providing features like speaker diarization.
</Card>
<Card title="Voice Cloning" href="https://elevenlabs.io/docs/capabilities/voice-cloning">
Create stunningly accurate digital replicas of voices from audio samples,
with both instant and professional-grade options to suit your needs.
</Card>
<Card title="Voice Design" href="https://elevenlabs.io/docs/capabilities/voice-design">
Craft entirely new, unique synthetic voices based on descriptive parameters,
enabling custom voice creation without requiring sample recordings.
</Card>
<Card title="Conversational AI Platform" href="https://elevenlabs.io/docs/conversational-ai/overview">
Build and deploy end-to-end conversational voice agents, integrating STT,
LLMs (like GPT, Claude, Gemini), TTS, and turn-taking logic.
</Card>
<Card title="Dubbing" href="https://elevenlabs.io/docs/capabilities/dubbing">
Automatically dub audio or video content into different languages while
preserving the original voice characteristics.
</Card>
<Card title="Sound Effects" href="https://elevenlabs.io/docs/capabilities/sound-effects">
Create custom sound effects and ambient audio from simple text descriptions,
adding rich audio elements to your applications.
</Card>
<Card title="Voice Library" href="https://elevenlabs.io/voice-library">
Access an extensive collection of pre-made, ready-to-use voices contributed
by the ElevenLabs community.
</Card>
</Cards>
## Use Cases
<Cards>
<Card title="Real-time Voice Agents">
Power conversational AI applications like customer service bots, virtual
assistants, or interactive characters with low-latency TTS.
</Card>
<Card title="Audiobook & Narration">
Create professional-quality narration for audiobooks, articles, videos, and
e-learning content in multiple languages and voices. Experience this in the
[TTS Demo](/ai/docs/tts).
</Card>
<Card title="Accessibility">
Enhance digital accessibility by converting text content into natural
speech, making your applications more inclusive for users with visual
impairments or reading difficulties.
</Card>
<Card title="Personalized Content">
Deliver dynamic, personalized audio experiences with custom-designed or
cloned voices, creating unique and engaging user interactions.
</Card>
<Card title="Global Content Creation">
Utilize dubbing and multilingual TTS to easily adapt content for
international audiences.
</Card>
<Card title="Gaming & Entertainment">
Generate character voices, ambient sounds, and dynamic audio for immersive
experiences.
</Card>
</Cards>
## Links
* [ElevenLabs Website](https://elevenlabs.io/)
* [ElevenLabs Documentation](https://elevenlabs.io/docs)
* [Developer Quickstart](https://elevenlabs.io/docs/quickstart)
* [API Reference](https://elevenlabs.io/docs/api-reference/introduction)
* [Pricing](https://elevenlabs.io/pricing)

View File

@@ -0,0 +1,121 @@
---
title: Google AI
description: Setup Google Generative AI provider and learn how to use its models like Gemini in the starter kit.
url: /ai/docs/google
---
# Google AI
The [Google Generative AI](https://ai.google/) provider integrates Google's state-of-the-art models, including the versatile Gemini family, into your applications through the AI SDK.
![Google Generative AI](/images/docs/ai/providers/google.webp)
## Setup
<Steps>
<Step>
### Generate API Key
Visit the [Google AI Studio](https://aistudio.google.com/app/apikey) to create your API key. For enterprise applications using Google Cloud, you can alternatively configure authentication via Application Default Credentials or service accounts.
</Step>
<Step>
### Add API Key to Environment
Add your API key to your project's `.env` file (e.g., in `apps/web`):
```bash title=".env"
GOOGLE_GENERATIVE_AI_API_KEY=your-api-key
```
If using Google Cloud credentials instead, ensure they're properly configured in your environment.
</Step>
<Step>
### Configure Provider (Optional)
The starter kit automatically uses the `GOOGLE_GENERATIVE_AI_API_KEY` environment variable. For advanced configurations (such as proxies, custom API versions, or specific headers), you can create a tailored provider instance using `createGoogleGenerativeAI`. See the [AI SDK Google documentation](https://sdk.vercel.ai/providers/ai-sdk-providers/google-generative-ai#provider-instance) for comprehensive details.
</Step>
</Steps>
## Features
<Cards>
<Card title="Language Models (Gemini)" href="https://sdk.vercel.ai/providers/ai-sdk-providers/google-generative-ai#language-models">
Leverage Google's advanced Gemini models for chat, text generation,
reasoning, and complex instruction following.
</Card>
<Card title="Embedding Models" href="https://sdk.vercel.ai/providers/ai-sdk-providers/google-generative-ai#embedding-models">
Utilize text embedding models to convert text into numerical representations
for tasks like semantic search, clustering, and RAG.
</Card>
<Card title="Vision / File Input" href="https://sdk.vercel.ai/providers/ai-sdk-providers/google-generative-ai#file-inputs">
Analyze and understand various file types (including images and PDFs)
alongside text prompts, enabling rich multimodal applications with
comprehensive content understanding.
</Card>
<Card title="Tool Usage / Function Calling" href="https://sdk.vercel.ai/docs/concepts/tools">
Empower models to interact seamlessly with external tools and APIs, allowing
them to perform real-world actions and retrieve up-to-date information for
more capable applications.
</Card>
<Card title="Safety Settings" href="https://sdk.vercel.ai/providers/ai-sdk-providers/google-generative-ai#safety-ratings">
Configure safety thresholds to control model responses regarding harmful
content categories. Access safety ratings in the response metadata.
</Card>
<Card title="Cached Content" href="https://sdk.vercel.ai/providers/ai-sdk-providers/google-generative-ai#cached-content">
Cache content to optimize context reuse and potentially reduce latency and
costs for repeated queries with similar context.
</Card>
<Card title="Search Grounding" href="https://sdk.vercel.ai/providers/ai-sdk-providers/google-generative-ai#search-grounding">
(With compatible models) Ground responses in real-time search results,
dramatically enhancing factual accuracy and providing up-to-date information
on current topics.
</Card>
</Cards>
## Use Cases
<Cards>
<Card title="AI Chatbot">
Create sophisticated conversational agents powered by Gemini models that can
engage in natural dialogue and handle complex, multi-step tasks. Experience
this in our [Chat Demo](/ai/docs/chat).
</Card>
<Card title="Content Generation">
Generate diverse text formats, from creative writing and marketing copy to
code explanations and summaries.
</Card>
<Card title="Multimodal Applications">
Build applications that seamlessly analyze and understand images, documents,
and other file types alongside text, creating richer, more contextual user
experiences.
</Card>
<Card title="Semantic Search & RAG">
Implement powerful search capabilities or sophisticated Retrieval-Augmented
Generation systems using Google's high-performance embedding models for more
accurate information retrieval.
</Card>
<Card title="Automated Workflows">
Streamline operations by connecting language models to external tools and
APIs through function calling, automating complex business processes and
repetitive tasks with minimal human intervention.
</Card>
</Cards>
## Links
* [Google AI](https://ai.google/)
* [Google AI Studio](https://aistudio.google.com/)
* [Google Generative AI Documentation](https://ai.google.dev/docs)
* [AI SDK - Google Provider Docs](https://sdk.vercel.ai/providers/ai-sdk-providers/google-generative-ai)

View File

@@ -0,0 +1,116 @@
---
title: Image Generation
description: Learn how to generate images using AI models within the TurboStarter AI demo application.
url: /ai/docs/image
---
# Image Generation
The [Image Generation](https://ai.turbostarter.dev/image) demo application allows users to create unique visuals from textual descriptions using various AI models. It provides a simple interface to input prompts, select models, and view generated images.
<AIAppShowcase id="image" />
## Features
Explore the capabilities of the AI-powered image generation tool:
<Cards>
<Card title="Prompt-based generation">
Create images simply by describing what you want to see in text.
</Card>
<Card title="Multi-model support">
Choose from different AI image generation models offered by various
providers.
</Card>
<Card title="Aspect ratio control">
Select the desired aspect ratio for your generated images (e.g. square,
landscape, portrait).
</Card>
<Card title="Batch generation">
Create multiple design variations from a single prompt simultaneously,
accelerating your creative workflow.
</Card>
<Card title="Generation history">
Access and reference your complete generation history, including all prompts
and resulting images for continued iteration.
</Card>
</Cards>
## Setup
To implement image generation in your application, you'll need to configure the necessary backend services.
<Cards>
<Card href="/ai/docs/database" title="Database" description="Configure a PostgreSQL database to store generation history and image metadata." />
<Card href="/ai/docs/storage" title="Storage" description="Set up S3-compatible storage to securely manage generated image assets." />
</Cards>
You'll also need API keys for your preferred AI models. Follow the detailed setup instructions in the provider documentation linked below.
## AI models
The Image Generation app leverages the AI SDK to support various models capable of creating images from text. Configure the providers for the models you wish to use:
<Cards>
<Card href="/ai/docs/openai" title="OpenAI" description="Implement DALL·E models for exceptional image quality and creative fidelity." icon={<OpenAI />} />
<Card href="/ai/docs/replicate" title="Replicate" description="Access a diverse ecosystem of open-source models including Stable Diffusion variants." icon={<Replicate />} />
</Cards>
For detailed implementation guidance, refer to the [AI SDK documentation](https://sdk.vercel.ai/docs/ai-sdk-core/image-generation) covering the `generateImage` function and supported providers.
## Data persistence
Details about image generation requests and the resulting images are stored to maintain user history.
<Card href="/ai/docs/database" title="Database" description="Learn more about database services in TurboStarter AI." />
Data is organized within a dedicated PostgreSQL schema named `image`:
* `generations`: captures detailed information about each generation request, including the `prompt`, selected `model`, `aspectRatio`, requested image `count`, `userId`, and precise timestamps.
* `images`: stores complete metadata for each generated image, linked to its parent `generation` record via `generationId` and maintaining the `url` reference to the stored image file.
<Card href="/ai/docs/storage" title="Storage" description="Learn more about cloud storage services in TurboStarter AI." />
The generated image files are securely stored in [cloud storage](/ai/docs/storage) (S3-compatible). Each image's location is tracked via the `url` field in the `images` table for reliable retrieval.
## Structure
The Image Generation feature is architected across the monorepo for optimal code organization and reusability.
### Core
The `@turbostarter/ai` package (`packages/ai`) contains the essential logic under `modules/image`:
* Comprehensive types, validation schemas (for prompts, aspect ratios, etc.), and constants
* Core API logic for processing image generation requests and interfacing with AI models
* Database operations for recording generation details and image metadata
* Utilities for uploading generated images to cloud storage
### API
The `packages/api` package defines the backend API endpoints using Hono:
* `src/modules/ai/image/image.router.ts`: implements Hono RPC routes for image generation, handles input validation, applies necessary middleware (authentication, credit management), and invokes the core logic from `@turbostarter/ai`.
### Web
The Next.js application (`apps/web`) delivers an intuitive user interface:
* `src/app/[locale]/(apps)/image/**`: contains the Next.js App Router pages and layouts for the image generation experience
* `src/components/image/**`: houses reusable React components tailored to the image generation UI (prompt input, model selector, image gallery, etc.)
### Mobile
The Expo/React Native application (`apps/mobile`) provides a native mobile experience:
* `src/app/image/**`: defines the screens for the mobile image generation interface
* `src/components/image/**`: contains React Native components optimized for mobile interaction
* **API integration**: utilizes the same Hono RPC client (`packages/api`) as the web app for consistent backend communication
This architecture ensures perfect consistency across platforms while enabling tailored UI implementations optimized for each environment.

View File

@@ -0,0 +1,39 @@
---
title: Internationalization
description: Learn how we manage internationalization in TurboStarter AI.
url: /ai/docs/internationalization
---
# Internationalization
TurboStarter AI builds on the core internationalization (i18n) setup from the main TurboStarter framework. The shared `@turbostarter/i18n` package in `packages/i18n` handles translation management across platforms.
This gives you the benefit of a proven system using [i18next](https://www.i18next.com/) for managing translations on both web and mobile apps. Plus, the AI models and LLMs integrated within TurboStarter AI generally support multiple languages, enabling interactions beyond what's covered by UI translations alone.
For detailed information on configuring languages, adding translations, or using the `useTranslation` hook, check out the core documentation:
<Cards>
<Card title="Web internationalization" description="Learn about i18n setup for the Next.js web app." href="/docs/web/internationalization/overview" />
<Card title="Mobile internationalization" description="Learn about i18n setup for the React Native (Expo) mobile app." href="/docs/mobile/internationalization" />
</Cards>
## AI-specific translations
While most translations are shared across the platform, TurboStarter AI introduces a dedicated `ai` namespace within translation files. This namespace contains strings specifically for AI features, demo applications, and UI elements unique to the AI starter kit.
```json title="packages/i18n/locales/en/ai.json"
{
"chat": {
"title": "AI Chatbot",
"description": "Engage in intelligent conversations."
},
"image": {
"title": "Image Generation",
"description": "Create stunning visuals with AI."
}
// ... other AI-specific translations
}
```
When adding translations for new AI features or modifying existing ones, place them within the `ai` namespace in the appropriate language files (e.g., `en/ai.json`, `es/ai.json`). This keeps AI-related text organized and separate from core application translations.

View File

@@ -0,0 +1,125 @@
---
title: Meta
description: Setup Meta's Llama models and learn how to use them in the starter kit via various hosting providers.
url: /ai/docs/meta
---
# Meta
The [Meta](https://ai.meta.com/) provider integration brings Meta's cutting-edge Llama family of open-weight models to your applications through the AI SDK. Renowned for their exceptional performance across diverse tasks, these models deliver state-of-the-art capabilities for your AI solutions.
![Meta Llama](/images/docs/ai/providers/meta.jpg)
## Setup
Deploying Llama models in your applications involves leveraging a third-party hosting provider that integrates seamlessly with the AI SDK, such as DeepInfra, Fireworks AI, Amazon Bedrock, Baseten, and others.
<Steps>
<Step>
### Choose a hosting provider & get API Key
Select a trusted provider that hosts Llama models (e.g., [DeepInfra](https://deepinfra.com/), [Fireworks AI](https://fireworks.ai/), or [Amazon Bedrock](https://aws.amazon.com/bedrock/)). Register with your preferred provider and generate a secure API key through their platform console.
</Step>
<Step>
### Add API Key to environment
Add your provider-specific API key to your project's `.env` file (e.g., in `apps/web`). Use the appropriate environment variable for your chosen provider:
```bash title=".env"
# Example for DeepInfra
DEEPINFRA_API_KEY=your-deepinfra-api-key
# Example for Fireworks AI
FIREWORKS_API_KEY=your-fireworks-api-key
# Example for Amazon Bedrock (requires AWS credentials)
# AWS_ACCESS_KEY_ID=...
# AWS_SECRET_ACCESS_KEY=...
# AWS_REGION=...
```
</Step>
<Step>
### Configure provider
When implementing AI SDK functions (`generateText`, `streamText`, etc.), initialize the client for your selected provider and specify the appropriate Llama model identifier:
```ts
import { generateText } from "ai";
import { deepinfra } from "@ai-sdk/deepinfra";
// Or: import { fireworks } from '@ai-sdk/fireworks';
// Or: import { bedrock } from '@ai-sdk/amazon-bedrock';
const { text } = await generateText({
// Example using DeepInfra
model: deepinfra("meta-llama/Meta-Llama-3.1-8B-Instruct"),
// Example using Fireworks AI
// model: fireworks('accounts/fireworks/models/llama-v3p1-8b-instruct'),
// Example using Amazon Bedrock
// model: bedrock('meta.llama3-1-8b-instruct-v1:0'),
prompt: "Why is the sky blue?",
});
```
For comprehensive implementation details, consult the AI SDK documentation for your specific provider: [DeepInfra](https://sdk.vercel.ai/providers/ai-sdk-providers/deepinfra), [Fireworks AI](https://sdk.vercel.ai/providers/ai-sdk-providers/fireworks), [Amazon Bedrock](https://sdk.vercel.ai/providers/ai-sdk-providers/amazon-bedrock), etc.
</Step>
</Steps>
## Features
Llama models accessible through the AI SDK offer a range of powerful capabilities, with specific features varying based on model version and hosting provider implementation.
<Cards>
<Card title="Chat Models" href="https://sdk.vercel.ai/docs/guides/llama-3_1">
Utilize Llama's instruction-tuned models for dialogue generation,
translation, reasoning, and other conversational tasks. Available in various
sizes (e.g., 8B, 70B, 405B).
</Card>
<Card title="Tool Usage / Function Calling" href="https://sdk.vercel.ai/docs/guides/llama-3_1#using-tools-with-the-ai-sdk">
Empower Llama models to interact with external tools and functions, enabling
complex, multi-step task execution and real-world system integration.
(Capabilities may vary depending on your selected provider).
</Card>
<Card title="Reasoning & Code Generation" href="https://ai.meta.com/blog/meta-llama-3-1/">
Leverage Llama's capabilities for complex reasoning problems and generating
code snippets in various programming languages.
</Card>
</Cards>
## Use Cases
<Cards>
<Card title="AI Chatbot">
Create intelligent, responsive chatbots capable of natural conversations,
accurate information retrieval, and efficient task execution. Experience
this capability in our [Chat Demo](/ai/docs/chat).
</Card>
<Card title="Content Generation">
Produce diverse, high-quality text content spanning articles, summaries,
creative narratives, marketing copy, and more—tailored to your specific
requirements.
</Card>
<Card title="Code Assistance">
Boost developer productivity with AI-powered code generation, insightful
code explanations, effective debugging assistance, and programming guidance
across multiple languages.
</Card>
<Card title="Automated Workflows">
Streamline operations by combining Llama models with tool usage capabilities
to automate complex business processes and seamlessly interact with your
existing systems.
</Card>
</Cards>
## Links
* [Meta AI](https://ai.meta.com/)
* [Meta Llama Models](https://ai.meta.com/llama/)
* [AI SDK - Llama 3.1 Guide](https://sdk.vercel.ai/docs/guides/llama-3_1)
* [AI SDK - Providers](https://sdk.vercel.ai/providers) (Find hosting provider docs here)

View File

@@ -0,0 +1,122 @@
---
title: OpenAI
description: Setup OpenAI provider and learn how to use it in the starter kit.
url: /ai/docs/openai
---
# OpenAI
The [OpenAI](https://openai.com) provider integrates OpenAI's powerful suite of language models, image generation capabilities, and embedding technologies into your application through the AI SDK.
![OpenAI](/images/docs/ai/providers/openai.png)
## Setup
<Steps>
<Step>
### Generate API Key
Visit the [OpenAI API keys page](https://platform.openai.com/api-keys) to create your personal secret key for API access.
</Step>
<Step>
### Add API Key to Environment
Add your API key to your project's `.env` file (e.g., in `apps/web`):
```bash title=".env"
OPENAI_API_KEY=your-api-key
```
</Step>
<Step>
### Configure Provider (Optional)
By default, the starter kit automatically uses the `OPENAI_API_KEY` environment variable. For advanced configurations (such as using a proxy or specific organization ID), you can customize the provider instance. For detailed options, refer to the [AI SDK OpenAI documentation](https://sdk.vercel.ai/providers/ai-sdk-providers/openai#provider-instance).
</Step>
</Steps>
## Features
<Cards>
<Card title="Chat Models" href="https://sdk.vercel.ai/providers/ai-sdk-providers/openai#language-models">
Leverage state-of-the-art models for building sophisticated conversational
AI, generating creative text formats, and answering complex questions.
</Card>
<Card title="Embedding Models" href="https://sdk.vercel.ai/providers/ai-sdk-providers/openai#embedding-models">
Transform text into rich numerical representations with powerful models like
`text-embedding-3-large`, enabling advanced semantic search, intelligent
text clustering, and highly personalized recommendation systems.
</Card>
<Card title="Image Generation (DALL·E)" href="https://sdk.vercel.ai/providers/ai-sdk-providers/openai#image-generation-models">
Generate unique images from textual descriptions using OpenAI's DALL·E
models, enabling creative applications and content generation.
</Card>
<Card title="Speech Generation (TTS)" href="https://sdk.vercel.ai/providers/ai-sdk-providers/openai#speech-generation-models">
Convert written text into natural-sounding human speech with various voices
using Text-to-Speech (TTS) models, ideal for accessibility features or voice
interfaces.
</Card>
<Card title="Vision / Image Input" href="https://sdk.vercel.ai/providers/ai-sdk-providers/openai#vision--image-input">
Empower models like GPT-4o or GPT-4 Turbo with Vision capabilities to
understand, analyze, and describe the content of images provided in prompts.
</Card>
<Card title="Tool Usage / Function Calling" href="https://sdk.vercel.ai/providers/ai-sdk-providers/openai#tool-usage--function-calling">
Allow language models to intelligently interact with your external tools,
APIs, and custom functions, orchestrating complex multi-step tasks and
creating powerful AI agents that can take actions in the real world.
</Card>
</Cards>
## Use Cases
<Cards>
<Card title="AI Chatbot">
Create intelligent, context-aware conversational agents that engage in
natural dialogue, answer complex questions, and complete sophisticated tasks
based on user needs. Experience this capability in our [Chat
Demo](/ai/docs/chat).
</Card>
<Card title="Content Generation">
Automate the creation of diverse text-based content, including blog posts,
marketing copy, emails, code snippets, and creative writing pieces.
</Card>
<Card title="Semantic Search & RAG">
Build advanced search systems that truly understand the meaning behind user
queries, enhanced with Retrieval-Augmented Generation (RAG) for delivering
exceptionally accurate, contextually relevant answers from your data.
</Card>
<Card title="Image Generation & Analysis">
Develop applications that can generate images from text prompts or analyze
and interpret the content of existing images for tagging, description, or
moderation. Check out the [Image Generation Demo](/ai/docs/image).
</Card>
<Card title="Text-to-Speech Applications">
Design engaging voice-enabled experiences, including lifelike virtual
assistants, expressive audiobook narration, real-time translation services,
and accessibility tools that convert text to natural speech for visually
impaired users.
</Card>
<Card title="Automated Workflows">
Transform business processes by connecting powerful language models to your
existing tools and systems through function calling, automating complex
workflows for data processing, report generation, customer support, and
more.
</Card>
</Cards>
## Links
* [OpenAI Website](https://openai.com/)
* [OpenAI API Documentation](https://platform.openai.com/docs)
* [AI SDK - OpenAI Provider Docs](https://sdk.vercel.ai/providers/ai-sdk-providers/openai)

View File

@@ -0,0 +1,135 @@
---
title: Chat with PDF
description: Engage in conversations with your PDF documents using AI to extract insights and answer questions.
url: /ai/docs/pdf
---
# Chat with PDF
The [Chat with PDF](https://ai.turbostarter.dev/pdf) demo application enables intelligent interaction with document content through a conversational AI interface. Upload PDF files and instantly engage in natural dialogue about their contents, asking questions, requesting summaries, and extracting key information with remarkable accuracy.
<AIAppShowcase id="pdf" />
## Features
Transform how you interact with document content through these powerful capabilities:
<Cards>
<Card title="PDF upload">
Easily upload PDF files directly into the application for analysis.
</Card>
<Card title="Contextual conversation">
Chat with an AI that understands the content of your uploaded PDF, providing
relevant answers based on the text.
</Card>
<Card title="Information extraction">
Quickly find specific information, key points, or summaries within the
document through natural language queries.
</Card>
<Card title="Source highlighting (coming soon)">
Visualize exactly which document sections informed the AI's responses with
precise source highlighting.
</Card>
<Card title="Multi-document intelligence (coming soon)">
Conduct sophisticated conversations spanning multiple uploaded documents,
enabling cross-document analysis and comparison.
</Card>
</Cards>
## Setup
To implement the "Chat with PDF" application in your project, configure these essential backend services:
<Cards>
<Card href="/ai/docs/database" title="Database">
Set up PostgreSQL with the `pgvector` extension to efficiently store
conversation history, document metadata, and vector embeddings for semantic
search.
</Card>
<Card href="/ai/docs/storage" title="Storage">
Configure S3-compatible cloud storage for secure management of uploaded PDF
documents.
</Card>
</Cards>
You'll also need to obtain API keys for both the conversational AI models and the embedding models used for text processing.
## AI models
This application leverages two complementary AI model types working together:
1. **Large Language Models (LLMs):** Provide sophisticated natural language understanding to interpret your questions and generate contextually appropriate responses based on document content.
2. **Embedding Models:** Convert document text segments into numerical vector representations that enable efficient semantic similarity search and [Retrieval-Augmented Generation (RAG)](https://en.wikipedia.org/wiki/Retrieval-augmented_generation).
Configure the providers for the models you wish to use:
<Cards>
<Card href="/ai/docs/openai" title="OpenAI" description="Utilize GPT models for conversational AI and advanced embedding models for vector representation." icon={<OpenAI />} />
<Card href="/ai/docs/anthropic" title="Anthropic" description="Implement Claude models for sophisticated reasoning and nuanced document understanding." icon={<Anthropic />} />
<Card href="/ai/docs/google" title="Google AI" description="Leverage Gemini models for powerful conversational capabilities with document content." icon={<Google />} />
<Card href="/ai/docs/replicate" title="Replicate" description="Access diverse open-source embedding models for flexible implementation options." icon={<Replicate />} />
</Cards>
For comprehensive configuration details, consult the [AI SDK documentation](https://sdk.vercel.ai/docs) covering provider setup and model selection.
## Data persistence
The application stores data related to chats, documents, and embeddings to provide a persistent experience.
<Card href="/ai/docs/database" title="Database" description="Learn more about database services in TurboStarter AI." />
Application data is organized within a dedicated PostgreSQL schema named `pdf`:
* `chats`: captures essential metadata for each document-specific conversation session.
* `messages`: stores all user queries and AI responses within conversation threads.
* `documents`: maintains comprehensive tracking of uploaded PDF files, including filenames and storage locations.
* `embeddings`: contains text segments extracted from PDFs along with their vector representations (using [`pgvector`](https://github.com/pgvector/pgvector)'s `vector` data type). To optimize similarity searches critical for RAG processing, the system creates an index (`embeddingIndex` using [HNSW](https://github.com/pgvector/pgvector#hnsw)) on the `embedding` column.
<Card href="/ai/docs/storage" title="Storage" description="Learn more about cloud storage services in TurboStarter AI." />
The PDF files uploaded by users are securely stored in your configured [cloud storage](/ai/docs/storage) bucket. The `path` field in the `documents` table maintains the precise reference to each file's location.
## Structure
The "Chat with PDF" feature is architected across the monorepo for optimal organization and code reuse:
### Core
The `@turbostarter/ai` package (`packages/ai`) contains the essential logic under `modules/pdf`:
* Comprehensive types, validation schemas, and constants specific to PDF processing
* Advanced document parsing, text segmentation, and embedding generation utilities
* Core API logic for managing conversations, performing RAG-based lookups, and interacting with LLMs
* Database operations for storing and retrieving conversations, documents, and embeddings
* Shared utilities for managing PDF file uploads and downloads
### API
The `packages/api` package defines the backend API endpoints using [Hono](https://hono.dev/):
* `src/modules/ai/pdf/pdf.router.ts`: implements Hono RPC routes for document upload and conversation management, handles input validation, applies middleware (authentication, credit management), and invokes the core functionality from `@turbostarter/ai`.
### Web
The [Next.js](https://nextjs.org/) application (`apps/web`) delivers an intuitive user interface:
* `src/app/[locale]/(apps)/pdf/**`: contains the Next.js App Router pages and layouts for the document conversation experience
* `src/components/pdf/**`: houses reusable React components specific to the PDF interaction UI (document upload, conversation interface, message display)
### Mobile
The [Expo](https://expo.dev/)/[React Native](https://reactnative.dev/) application (`apps/mobile`) provides a native mobile experience:
* `src/app/pdf/**`: defines the screens for the mobile document conversation interface
* `src/components/pdf/**`: contains React Native components optimized for mobile document interaction
* **API integration**: utilizes the same Hono RPC client (`packages/api`) as the web app for consistent backend communication
This architecture ensures that core AI processing and data handling logic is shared across platforms, while enabling optimized UI implementations tailored to each environment.

View File

@@ -0,0 +1,85 @@
---
title: Replicate
description: Setup Replicate provider and learn how to use it in the starter kit.
url: /ai/docs/replicate
---
# Replicate
The [Replicate](https://replicate.com) provider unlocks access to an extensive library of open-source AI models through a streamlined cloud API, seamlessly integrated with the AI SDK. It's particularly well-known for image generation capabilities.
![Replicate](/images/docs/ai/providers/replicate.png)
## Setup
<Steps>
<Step>
### Generate API Key
Visit the [Replicate website](https://replicate.com/), create an account or sign in, then navigate to your account settings to generate your personal API token.
</Step>
<Step>
### Add API Key to Environment
Add your API token to your project's `.env` file (e.g., in `apps/web`):
```bash title=".env"
REPLICATE_API_TOKEN=your-api-key
```
</Step>
<Step>
### Configure Provider (Optional)
The starter kit automatically uses the `REPLICATE_API_TOKEN` environment variable. For advanced configurations (such as proxies or custom headers), you can create a tailored provider instance. For comprehensive details, refer to the [AI SDK Replicate documentation](https://sdk.vercel.ai/providers/ai-sdk-providers/replicate#provider-instance).
</Step>
</Steps>
## Features
<Cards>
<Card title="Run Open-Source Models" href="https://replicate.com/explore">
Gain instant access to a diverse ecosystem of community-contributed models
spanning text generation, image creation, audio processing, video synthesis,
and numerous other AI capabilities.
</Card>
<Card title="Image Generation" href="https://sdk.vercel.ai/providers/ai-sdk-providers/replicate#image-models">
Create stunning visuals using various state-of-the-art open-source models
directly through the AI SDK's intuitive `generateImage` function, with
support for specific model versions and custom parameters.
</Card>
<Card title="Model-Specific Options" href="https://sdk.vercel.ai/providers/ai-sdk-providers/replicate#model-specific-options">
Fine-tune model behavior by passing specific parameters via
`providerOptions.replicate`, allowing precise control over generation
settings according to each model's unique capabilities.
</Card>
</Cards>
## Use Cases
<Cards>
<Card title="Image Generation">
Create unique visuals, artwork, or variations based on text prompts using a
diverse set of image models. Check out the [Image Generation
Demo](/ai/docs/image).
</Card>
<Card title="Access Niche Models">
Utilize specialized open-source models for specific tasks that might not be
available through other major providers.
</Card>
<Card title="AI Experimentation">
Quickly experiment with different community-published models for various AI
tasks without managing infrastructure.
</Card>
</Cards>
## Links
* [Replicate Website](https://replicate.com)
* [Replicate Documentation](https://replicate.com/docs)
* [AI SDK - Replicate Provider Docs](https://sdk.vercel.ai/providers/ai-sdk-providers/replicate)

View File

@@ -0,0 +1,57 @@
---
title: Security
description: Learn about the security measures implemented in TurboStarter AI.
url: /ai/docs/security
---
# Security
<Callout>
Remember to regularly review your security implementations and update them as needed.
</Callout>
The starter kit incorporates several security measures to protect your application and users when interacting with AI services.
## Authenticated endpoints
All AI operation endpoints require user authentication. This is enforced through middleware that verifies the user's session before granting access to any AI features.
<Card title="Authentication" href="/ai/docs/auth" description="Learn more about the authentication setup in TurboStarter AI." />
The system creates anonymous sessions by default, but you can implement stronger authentication using the core framework's capabilities or the dedicated [authentication setup](/docs/web/auth/overview).
## Credit-based access
To prevent AI resource abuse, TurboStarter AI includes a credit-based system. Users receive a limited number of credits that are consumed when using AI features.
<Card title="Billing" href="/ai/docs/billing" description="Learn more about the billing and credits system." />
This approach avoids misuse while enabling potential monetization. Learn about the implementation details in the [Core billing documentation](/docs/web/billing/overview).
## Rate limiting
API endpoints are guarded by rate limiting to prevent abuse and ensure fair usage. This protects your application from potential denial-of-service attacks and excessive request volumes.
<Card title="API" href="/ai/docs/api" description="Learn more about the API layer and services in TurboStarter AI." />
We use [`hono-rate-limiter`](https://github.com/rhinobase/hono-rate-limiter), which supports various storage options including [Redis](https://redis.io/), [Cloudflare KV](https://developers.cloudflare.com/workers/runtime-apis/kv/), and [Memcached](https://memcached.org/) for distributed rate limiting.
## Secure API key handling
Sensitive API keys for AI providers ([OpenAI](/ai/docs/openai), [Anthropic](/ai/docs/anthropic), [Google AI](/ai/docs/google), etc.) are managed exclusively on the backend.
They are **NEVER** exposed to client-side code, dramatically reducing the risk of key leakage or unauthorized usage.
## AI service abuse protection
While TurboStarter AI provides application-level safeguards like credit limits and rate limiting, it's essential to implement additional protection directly with your AI providers.
<Callout type="warn" title="Set limits and alerts">
Always configure spending limits, usage quotas, and monitoring alerts in your
AI provider dashboards (e.g., [OpenAI](/ai/docs/openai),
[Anthropic](/ai/docs/anthropic), [Google AI](/ai/docs/google)). These serve as
critical safety nets against unexpected costs or potential abuse that might
bypass your application-level controls.
</Callout>
By combining application-level security with provider-level controls, you'll build truly robust and secure AI applications.

View File

@@ -0,0 +1,77 @@
---
title: Tech stack
description: Learn which tools and libraries power TurboStarter AI.
url: /ai/docs/stack
---
# Tech stack
## Turborepo
[Turborepo](https://turbo.build/) is a high-performance monorepo tool that optimizes dependency management and script execution across your project. We chose this monorepo setup to simplify feature management and enable seamless code sharing between packages.
<Card href="https://turbo.build/" title="Turborepo - Make Ship Happen" description="turbo.build" icon={<Turborepo />} />
## Next.js
[Next.js](https://nextjs.org) is a powerful [React](https://react.dev) framework that delivers server-side rendering, static site generation, and more. We selected Next.js for its exceptional flexibility and developer experience. It also serves as the foundation for our serverless API.
<Cards>
<Card href="https://react.dev" title="React" description="react.dev" icon={<React />} />
<Card href="https://nextjs.org" title="Next.js" description="nextjs.org" icon={<Next />} />
</Cards>
## React Native + Expo
[React Native](https://reactnative.dev/) is a leading open-source framework created by Facebook that enables building native mobile applications using [React](https://react.dev). It provides access to native platform capabilities while maintaining the development efficiency of React.
[Expo](https://expo.dev/) extends React Native with a comprehensive toolkit that streamlines development, building, and deployment of iOS, Android, and web apps from a single codebase.
<Cards className="grid-cols-2">
<Card href="https://reactnative.dev/" title="React Native" description="reactnative.dev" icon={<React />} />
<Card href="https://expo.dev/" title="Expo" description="expo.dev" icon={<Expo />} />
</Cards>
## AI SDK
[Vercel AI SDK](https://sdk.vercel.ai/) provides a robust toolkit for building AI-powered applications. It offers essential utilities and components for integrating advanced AI features, including streaming responses, interactive chat interfaces, and more.
<Card href="https://sdk.vercel.ai/" title="Vercel AI SDK" description="sdk.vercel.ai" icon={<AISDK />} />
## LangChain
[LangChain](https://js.langchain.com/) is a sophisticated framework designed for language model-powered applications. It delivers critical abstractions and tools for building complex AI systems, including prompt management, memory systems, and agent architectures.
<Card href="https://js.langchain.com/" title="LangChain" description="js.langchain.com" icon={<Langchain />} />
## Hono
[Hono](https://hono.dev) is an ultrafast, lightweight web framework optimized for edge computing. It includes a type-safe RPC client for secure function calls from the frontend. We leverage Hono to create efficient serverless API endpoints.
<Card href="https://hono.dev" title="Hono" description="hono.dev" icon={<Hono />} />
## Tailwind CSS
[Tailwind CSS](https://tailwindcss.com) is a utility-first CSS framework that accelerates UI development without writing custom CSS. We complement it with [Radix UI](https://radix-ui.com), a collection of accessible headless components, and [shadcn/ui](https://ui.shadcn.com), which lets you generate beautifully designed components with a single command.
<Cards className="grid-cols-2 sm:grid-cols-3">
<Card href="https://tailwindcss.com" title="Tailwind CSS" description="tailwindcss.com" icon={<Tailwind />} />
<Card href="https://radix-ui.com" title="Radix UI" description="radix-ui.com" icon={<Radix />} />
<Card href="https://ui.shadcn.com" title="shadcn/ui" description="ui.shadcn.com" icon={<Shadcn />} />
</Cards>
## Drizzle
[Drizzle](https://orm.drizzle.team/) is a type-safe, high-performance [ORM](https://orm.drizzle.team/docs/overview) (Object-Relational Mapping) for modern database management. It generates TypeScript types from your schema and enables fully type-safe queries.
We use [PostgreSQL](https://www.postgresql.org) as our default database, but Drizzle's flexibility allows you to easily switch to MySQL, SQLite, or any [other supported database](https://orm.drizzle.team/docs/connect-overview) by updating a few configuration lines.
<Cards>
<Card href="https://orm.drizzle.team/" title="Drizzle" description="orm.drizzle.team" icon={<Drizzle />} />
<Card href="https://www.postgresql.org" title="PostgreSQL" description="postgresql.org" icon={<Postgres />} />
</Cards>

View File

@@ -0,0 +1,34 @@
---
title: Storage
description: Explore cloud storage services for AI applications.
url: /ai/docs/storage
---
# Storage
Blob storage in TurboStarter AI offers a scalable solution for handling the diverse file types essential to modern AI applications. It works seamlessly with S3-compatible services including [AWS S3](https://aws.amazon.com/s3/), [Cloudflare R2](https://www.cloudflare.com/products/r2/), and [MinIO](https://min.io/).
## Use cases
Blob storage powers several key AI functions:
* **Managing user uploads:** safely storing files like PDFs or images that users upload for AI processing, as seen in the ["Chat with PDF" demo](/ai/docs/pdf) and image analysis features
* **Preserving AI-generated content:** storing outputs from AI models, such as images from the [Image Generation demo](/ai/docs/image) or audio files from the [Text-to-Speech demo](/ai/docs/tts)
* **Powering RAG systems:** housing documents and files that serve as knowledge sources for Retrieval-Augmented Generation, used in demos like [Chat with PDF](/ai/docs/pdf) and intelligent [Agents](/ai/docs/agents)
## Security
Properly configuring bucket permissions for your storage provider is critical. Always restrict access based on the principle of least privilege:
* Buckets containing user uploads or sensitive RAG documents should typically **not** be publicly accessible
* Set precise permissions that allow your application server (API) to read/write as needed while blocking unauthorized access
Refer to your provider's documentation ([AWS S3](https://docs.aws.amazon.com/AmazonS3/latest/userguide/security-best-practices.html), [Cloudflare R2](https://developers.cloudflare.com/security-center/security-insights/roles-and-permissions/), [MinIO](https://min.io/docs/minio/linux/administration/identity-access-management/policy-based-access-control.html)) for specific guidance on securing your storage buckets.
## Storage documentation
For detailed setup instructions, configuration options for different storage providers, and implementation best practices, check out the core storage documentation:
<Card title="Storage documentation" href="/docs/web/storage/overview" description="Learn how to configure and manage blob storage providers in the core TurboStarter documentation." />
In summary, blob storage is essential for building sophisticated AI applications - enabling you to handle user uploads, store AI-generated files, and manage RAG document collections.

View File

@@ -0,0 +1,89 @@
---
title: Text to Speech
description: Convert text into natural-sounding speech using advanced AI voice synthesis models.
url: /ai/docs/tts
---
# Text to Speech
The [Text to Speech (TTS)](https://ai.turbostarter.dev/tts) demo application transforms written text into high-quality spoken audio. It leverages state-of-the-art AI models to generate lifelike voices in various languages and styles.
<AIAppShowcase id="tts" />
## Features
Discover the powerful capabilities of this AI-powered voice synthesis solution:
<Cards>
<Card title="5,000+ voices">
Access a wide range of voices from providers like [Eleven
Labs](https://elevenlabs.io/), including different accents, ages, and
emotional tones, to find the perfect match for your content.
</Card>
<Card title="Real-time audio streaming">
Experience near-instantaneous audio generation with streaming delivery,
providing immediate feedback as your content comes to life.
</Card>
<Card title="Integrated audio player">
Enjoy a full-featured playback interface with precise controls for playback
speed and convenient options to download generated audio files.
</Card>
<Card title="Voice customization">
Fine-tune your audio output with adjustable parameters for pitch, speed, and
pauses, creating the most natural and engaging delivery possible (available
options vary by provider).
</Card>
<Card title="Intuitive user experience">
Benefit from a thoughtfully designed interface that makes transforming text
to speech effortless and efficient, even for first-time users.
</Card>
</Cards>
## AI models
This application primarily utilizes specialized text-to-speech models from [Eleven Labs](https://elevenlabs.io/).
<Cards>
<Card href="/ai/docs/eleven-labs" title="Eleven Labs" description="Integrate Eleven Labs' state-of-the-art voice synthesis technology for stunningly realistic and expressive speech generation." icon={<ElevenLabs />} />
</Cards>
For comprehensive information about available voices and advanced customization techniques, consult the [ElevenLabs SDK documentation](https://elevenlabs.io/docs/overview).
## Structure
The Text-to-Speech feature is organized across the monorepo for maximum flexibility and maintainability:
### Core
The `@turbostarter/ai` package (`packages/ai`) contains the essential logic under `modules/tts`:
* Comprehensive types, validation schemas, and constants specific to TTS functionality
* Core API logic for processing text-to-speech requests and interfacing with AI models
* Robust handling of generated audio file uploads to cloud storage
### API
The `packages/api` package defines the backend API endpoints using [Hono](https://hono.dev/):
* `src/modules/ai/tts/tts.router.ts`: implements Hono RPC routes for TTS generation, handles input validation, applies critical middleware (authentication, credit management), and invokes the core functionality from `@turbostarter/ai`.
### Web
The [Next.js](https://nextjs.org/) application (`apps/web`) provides the user interface:
* `src/app/[locale]/(apps)/tts/**`: contains the Next.js App Router pages and layouts for the TTS experience
* `src/components/tts/**`: houses reusable React components specific to the TTS interface (text input area, voice selector, audio player, etc.)
### Mobile
The [Expo](https://expo.dev/)/[React Native](https://reactnative.dev/) application (`apps/mobile`) provides the native mobile experience:
* `src/app/tts/**`: defines the screens for the mobile TTS interface
* `src/components/tts/**`: contains React Native components optimized for the mobile experience
* **API interaction**: utilizes the same Hono RPC client (`packages/api`) as the web app for consistent communication with the backend
This architecture ensures perfect consistency between platforms while allowing for optimized UI implementations tailored to each environment.

View File

@@ -0,0 +1,85 @@
---
title: UI
description: Learn more about UI components and design system in AI starter kit.
url: /ai/docs/ui
---
# UI
TurboStarter AI builds on the core TurboStarter UI foundation to create engaging interfaces for all AI features.
The UI architecture uses shared components and styles with platform-specific implementations:
* **`@turbostarter/ui`**: includes shared assets, themes, and fundamental styles
* **`@turbostarter/ui-web`**: contains web components built with [Tailwind CSS](https://tailwindcss.com), [Radix UI](https://www.radix-ui.com/), and [shadcn/ui](https://ui.shadcn.com)
* **`@turbostarter/ui-mobile`**: delivers mobile components using [Uniwind](https://uniwind.dev/) and [react-native-reusables](https://reactnativereusables.com/)
This approach maximizes code reuse while optimizing for each platform's unique capabilities.
## UI in AI applications
The AI starter kit leverages this foundation to create intuitive interfaces for various features and demo apps:
<Cards>
<Card title="Chat interfaces" className="shadow-none">
Components for displaying conversations, user input, and streaming responses
(used in [Chatbot](/ai/docs/chat) and [Chat with PDF](/ai/docs/pdf) demos).
</Card>
<Card title="Image galleries" className="shadow-none">
Displaying AI-generated images as masonry grids with options for interaction
(used in [Image Generation](/ai/docs/image) demo).
</Card>
<Card title="Input forms" className="shadow-none">
Structured forms for configuring AI tasks (e.g., selecting models, adjusting
parameters, modifying prompts).
</Card>
<Card title="Animations" className="shadow-none">
Visual feedback during AI processing, such as loading spinners or progress
indicators (e.g. [Text to Speech](/ai/docs/tts) voice avatar animation).
</Card>
<Card title="Feedback mechanisms" className="shadow-none">
UI elements for users to rate or provide feedback on AI outputs. This can
include thumbs up/down buttons or text input fields for comments.
</Card>
<Card title="Error handling" className="shadow-none">
Components for displaying error messages or alerts when AI tasks fail or
encounter issues.
</Card>
<Card title="Accessibility features" className="shadow-none">
Ensuring that all UI components are usable for individuals with
disabilities, including keyboard navigation and screen reader support.
</Card>
<Card title="Visualizations" className="shadow-none">
Components for displaying data or model outputs visually, such as charts,
graphs, or progress bars.
</Card>
</Cards>
## Generative UI
A standout aspect of AI applications is their ability to dynamically create or modify UI elements based on AI responses. TurboStarter AI enables this through:
* **AI SDK components**: libraries like the [AI SDK](https://sdk.vercel.ai/docs/introduction) provide specialized components and hooks (like `useActions` and `useUIState`) designed to render UI based on AI actions or structured data. This creates interactive elements—buttons, forms, or visualizations—that appear dynamically within conversations or workflows.
* **Structured output**: AI models can return data in specific formats (such as JSON) that your frontend parses to render appropriate components, display information, or trigger actions. For example, an AI might return product details that automatically render as interactive cards.
* **Conditional rendering**: the platform uses standard React patterns for showing, hiding, or transforming UI components based on AI interaction states. This creates smooth transitions between loading states, results displays, and follow-up options tailored to AI suggestions.
This approach delivers truly responsive user experiences where interfaces adapt intelligently to ongoing AI processes. The [Chat demo app](/ai/docs/chat) showcases these generative UI capabilities in action.
## Customization and further details
Customizing appearance (themes, styling) or adding new UI components follows the same process as core TurboStarter applications. For complete guides on styling, theme management, and component development, see our core documentation:
<Cards>
<Card title="Web UI customization" description="Learn how to customize styling and components for the web application." href="/docs/web/customization/styling" />
<Card title="Mobile UI customization" description="Learn how to customize styling and components for the mobile application." href="/docs/mobile/customization/styling" />
</Cards>
By leveraging the core UI system, TurboStarter AI ensures consistent user experiences across platforms while letting you focus on creating unique AI functionalities.

View File

@@ -0,0 +1,90 @@
---
title: xAI Grok
description: Setup xAI provider and learn how to use it in the starter kit.
url: /ai/docs/xai
---
# xAI Grok
The [xAI](https://x.ai) provider integrates Grok models into your application using the AI SDK.
![xAI Grok](/images/docs/ai/providers/xai.webp)
## Setup
<Steps>
<Step>
### Generate API Key
Visit the [xAI website](https://x.ai) to create an account. After signing in, navigate to your account settings to generate an API key.
</Step>
<Step>
### Add API Key to Environment
Once you've acquired an API key, add it to your project's `.env` file (e.g., in `apps/web`):
```bash title=".env"
XAI_API_KEY=your-api-key
```
</Step>
<Step>
### Configure Provider (Optional)
The starter kit automatically uses the `XAI_API_KEY` environment variable. For advanced configurations and customization options, refer to the comprehensive [AI SDK xAI documentation](https://sdk.vercel.ai/providers/ai-sdk-providers/xai#provider-instance).
</Step>
</Steps>
## Features
<Cards>
<Card title="Chat Models" href="https://sdk.vercel.ai/providers/ai-sdk-providers/xai#language-models">
Utilize xAI's language models for conversational AI, text generation, and
other natural language processing tasks.
</Card>
<Card title="Tool Usage / Function Calling" href="https://sdk.vercel.ai/providers/ai-sdk-providers/xai#tool-usage--function-calling">
Enable language models to interact with external tools and functions,
allowing for more complex and automated task execution.
</Card>
<Card title="Image Generation" href="https://sdk.vercel.ai/providers/ai-sdk-providers/xai#image-models">
Generate images based on textual descriptions using xAI's models.
</Card>
</Cards>
## Use Cases
<Cards>
<Card title="AI Chatbot">
Create intelligent chatbots that engage users in natural, informative
conversations powered by xAI's Grok models, delivering responsive and
contextually relevant interactions. Experience this capability in our [Chat
Demo](/ai/docs/chat).
</Card>
<Card title="Content Generation">
Produce diverse, high-quality text content across various formats and
styles, harnessing the unique characteristics and capabilities of Grok
models for creative and informational outputs.
</Card>
<Card title="Automated Workflows">
Streamline operations by connecting xAI's language models with your existing
tools through function calling, enabling sophisticated automation of complex
business processes and repetitive tasks.
</Card>
<Card title="Image Generation">
Design striking visuals and artwork directly from text descriptions using
xAI's image generation capabilities, enabling creative applications and rich
visual content. Explore our [Image Generation Demo](/ai/docs/image) to see
these features in action.
</Card>
</Cards>
## Links
* [xAI Website](https://x.ai)
* [AI SDK - xAI Provider Docs](https://sdk.vercel.ai/providers/ai-sdk-providers/xai)

View File

@@ -0,0 +1,84 @@
---
title: AI
description: Leverage AI in your TurboStarter extension.
url: /docs/extension/ai
---
# AI
When it comes to AI within the browser extension, we can differentiate two approaches:
* **Server + client**: Traditional implementation, same as for [web](/docs/web/ai/overview) and [mobile](/docs/mobile/ai), used to stream responses generated on the server to the client.
* **Chrome built-in AI**: An [experimental implementation](https://developer.chrome.com/docs/ai/built-in) of [Gemini Nano](https://blog.google/technology/ai/google-gemini-ai/#performance) that's built into new versions of the Google Chrome browser.
We recommend relying more on the traditional server + client approach, as it's more versatile and easier to implement. Chrome's built-in AI is a nice feature, but it's still experimental and has some limitations.
Of course, you can always implement a *hybrid* approach which combines both solutions to achieve the best results.
## Server + client
The traditional usage of AI integration in the browser extension is the same as for [web app](/docs/web/ai/configuration#client-side) and [mobile app](/docs/mobile/ai). We use the exact same [API endpoint](/docs/web/ai/configuration#api-endpoint), and we leverage streaming to display answers incrementally to the user as they're generated.
```tsx title="main.tsx"
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
const Popup = () => {
const { messages } = useChat({
transport: new DefaultChatTransport({
api: "/api/ai/chat",
}),
});
return (
<div>
{messages.map((message) => (
<div key={message.id}>
{message.parts.map((part, i) => {
switch (part.type) {
case "text":
return <div key={`${message.id}-${i}`}>{part.text}</div>;
}
})}
</div>
))}
</div>
);
};
export default Popup;
```
It's the most reliable and recommended way to use AI in the browser extension. Feel free to reuse or modify it to suit your specific needs.
## Chrome built-in AI
<Callout type="warn">
Chrome's implementation of [built-in AI with Gemini Nano](https://developer.chrome.com/docs/ai/built-in) is experimental and will change as they test and address feedback.
</Callout>
Chrome's built-in AI is a preview feature. To use it, you need Chrome version 127 or greater and you must enable these flags:
* [chrome://flags/#prompt-api-for-gemini-nano](chrome://flags/#prompt-api-for-gemini-nano): `Enabled`
* [chrome://flags/#optimization-guide-on-device-model](chrome://flags/#optimization-guide-on-device-model): `Enabled BypassPrefRequirement`
* [chrome://components/](chrome://components/): Click `Optimization Guide On Device Model` to download the model.
Once enabled, you'll be able to use `window.ai` to access the built-in AI and do things like this:
![Chrome built-in AI](/images/docs/extension/ai.gif)
You can even use a [dedicated provider](https://sdk.vercel.ai/providers/community-providers/chrome-ai) from the Vercel AI SDK ecosystem to simplify its usage. Please remember that this API is still in its early stages and might change in the future.
<Callout title="Available in every extension context!">
The best thing is that you can use this API in every part of your extension, e.g., popup, background service worker, etc.
It's completely safe to use on the client-side, as we're not exposing any sensitive data to the user (such as the API key in the traditional server + client approach).
</Callout>
To learn more, please check out the official [Chrome documentation](https://developer.chrome.com/docs/ai/built-in) and the articles listed below.
<Cards>
<Card href="https://developer.chrome.com/docs/ai/built-in" title="Get started with built-in AI" description="developer.chrome.com" />
<Card href="https://developer.chrome.com/docs/extensions/ai" title="Extensions and AI" description="developer.chrome.com" />
</Cards>

View File

@@ -0,0 +1,92 @@
---
title: Configuration
description: Learn how to configure extension analytics in TurboStarter.
url: /docs/extension/analytics/configuration
---
# Configuration
The `@turbostarter/analytics-extension` package offers a streamlined and flexible approach to tracking events in your TurboStarter extension using various analytics providers. It abstracts the complexities of different analytics services and provides a consistent interface for event tracking.
In this section, we'll guide you through the configuration process for each supported provider.
Note that the configuration is validated against a schema, so you'll see error messages in the console if anything is misconfigured.
## Providers
Below, you'll find detailed information on how to set up and use each supported provider. Choose the one that best suits your needs and follow the instructions in the respective accordion section.
<Accordions>
<Accordion title="Google Analytics" id="google-analytics">
To use Google Analytics as your analytics provider, you need to [create a Google Analytics account](https://analytics.google.com/) and [set up a property](https://support.google.com/analytics/answer/9304153).
Next, add a data stream in your Google Analytics account settings:
1. Navigate to [Google Analytics](https://analytics.google.com/).
2. In the *Admin* section, under *Data collection and modification*, click on *Data Streams*.
3. Click *Add stream*.
4. Select *Web* as the platform.
5. Enter the required details for the stream (at minimum, provide a name and website URL).
6. Click *Create stream*.
After creating the stream, you'll need two pieces of information:
1. Your [Measurement ID](https://support.google.com/analytics/answer/12270356) (it should look like `G-XXXXXXXXXX`):
![Google Analytics Measurement ID](/images/docs/web/analytics/google/id.png)
2. Your [Measurement Protocol API secret](https://support.google.com/analytics/answer/9814495):
![Google Analytics Measurement Protocol API secret](/images/docs/web/analytics/google/api-secret.png)
Set these values in your `.env.local` file in the `apps/extension` directory and in your CI/CD provider secrets:
```dotenv
VITE_GOOGLE_ANALYTICS_MEASUREMENT_ID="your-measurement-id"
VITE_GOOGLE_ANALYTICS_SECRET="your-measurement-protocol-api-secret"
```
Also, make sure to activate the Google Analytics provider as your analytics provider by updating the exports in:
```ts title="index.ts"
// [!code word:google-analytics]
export * from "./google-analytics";
export * from "./google-analytics/env";
```
To customize the provider, you can find its definition in `packages/analytics/extension/src/providers/google-analytics` directory.
For more information, please refer to the [Google Analytics documentation](https://developers.google.com/analytics).
![Google Analytics dashboard](/images/docs/web/analytics/google/dashboard.jpg)
</Accordion>
<Accordion title="PostHog" id="posthog">
<Callout type="info" title="You can also use it for monitoring!">
PostHog is also one of pre-configured providers for [monitoring](/docs/extension/monitoring/overview) in TurboStarter. You can learn more about it [here](/docs/extension/monitoring/posthog).
</Callout>
To use PostHog as your analytics provider, you need to configure a PostHog instance. You can obtain the [Cloud](https://app.posthog.com/signup) instance by [creating an account](https://app.posthog.com/signup) or [self-host](https://posthog.com/docs/self-host) it.
Then, create a project and, based on your [project settings](https://app.posthog.com/project/settings), fill the following environment variables in your `.env.local` file in `apps/extension` directory and your CI/CD provider secrets:
```dotenv
VITE_POSTHOG_KEY="your-posthog-api-key"
VITE_POSTHOG_HOST="your-posthog-instance-host"
```
Also, make sure to activate the PostHog provider as your analytics provider by updating the exports in:
```ts title="index.ts"
// [!code word:posthog]
export * from "./posthog";
export * from "./posthog/env";
```
To customize the provider, you can find its definition in `packages/analytics/extension/src/providers/posthog` directory.
For more information, please refer to the [PostHog documentation](https://posthog.com/docs/advanced/browser-extension).
![PostHog dashboard](/images/docs/web/analytics/posthog.png)
</Accordion>
</Accordions>

View File

@@ -0,0 +1,59 @@
---
title: Overview
description: Get started with extension analytics in TurboStarter.
url: /docs/extension/analytics/overview
---
# Overview
When it comes to extension analytics, we can distinguish between two types:
* **Store listing analytics**: Used to track the performance of your extension's store listing (e.g., how many people have viewed your extension in the store or how many have installed it).
* **In-extension analytics**: Tracks user actions within your extension (e.g., how many users triggered your popup, how many users modified extension settings, etc.).
The `@turbostarter/analytics-extension` package provides a set of tools to easily implement both types of analytics in your extension.
## Store listing analytics
Interpreting your extension's store listing metrics can help you evaluate how changes to your extension and store listing affect conversion rates. For example, you can identify countries with a high number of visitors to prioritize supporting languages for those regions.
While each store implements a different set of metrics, there are some common ones you should be aware of:
* **Active installs**: The number of users who have installed your extension.
* **Active users**: The number of users who have used your extension.
* **Page views**: The number of times users have viewed your extension's detail page on the respective store.
To track more detailed metrics, you can opt in to Google Analytics in the Chrome Web Store's developer dashboard.
You can find this option under *Additional metrics* on the *Store listing* tab of your extension's control panel:
![Chrome Web Store - Store listing - Additional metrics](/images/docs/extension/analytics/opt-in-analytics.png)
<Callout>
The Chrome Web Store manages the account for you and makes the data available
in the Google Analytics dashboard.
</Callout>
By enabling this feature, you can optimize your extension's store listing based on metrics such as bounce rate, time on page, and more. This can lead to more installs and ultimately more users for your extension.
To learn more about the limitations of this type of analytics and how to adjust event details, please refer to the following sections in the official documentation:
<Cards>
<Card title="Analyze your store listing metrics" description="developer.chrome.com" href="https://developer.chrome.com/docs/webstore/metrics" />
<Card title="Use your Google Analytics account with the Chrome Web Store" description="developer.chrome.com" href="https://developer.chrome.com/docs/webstore/google-analytics" />
</Cards>
## In-extension analytics
TurboStarter comes with built-in support for tracking in-extension analytics. To learn more about each supported provider and how to configure them, see their respective sections:
<Cards>
<Card title="Google Analytics" href="/docs/extension/analytics/configuration#google-analytics" />
<Card title="PostHog" href="/docs/extension/analytics/configuration#posthog" />
</Cards>
All configuration and setup is built-in with a unified API, allowing you to switch between providers by simply changing the exports. You can even introduce your own provider without breaking any tracking-related logic.
In the following sections, we'll cover how to set up each provider and how to track events in your extension.

View File

@@ -0,0 +1,80 @@
---
title: Tracking events
description: Learn how to track events in your TurboStarter extension.
url: /docs/extension/analytics/tracking
---
# Tracking events
The strategy for tracking events that every provider has to implement is extremely simple:
```ts
export type AllowedPropertyValues = string | number | boolean;
type TrackFunction = (
event: string,
data?: Record<string, AllowedPropertyValues>,
) => void;
export interface AnalyticsProviderStrategy {
track: TrackFunction;
}
```
<Callout>
You don't need to worry much about this implementation, as all the providers are already configured for you. However, it's useful to be aware of this structure if you plan to add your own custom provider.
</Callout>
As shown above, each provider must supply the `track` function. This function is responsible for sending event data to the provider.
To track an event in any part of your extension, simply call the `track` method, passing the event name and an optional data object:
```tsx title="main.tsx"
import { track } from "@turbostarter/analytics-extension";
const Popup = () => {
return (
<button onClick={() => track("popup.button.click", { country: "US" })}>
Track event
</button>
);
};
export default Popup;
```
## Identifying users
Linking events to specific users enables you to build a full picture of how they're using your product across different sessions, devices, and platforms.
For identification purposes, we're extending the strategy with the `identify` and `reset` methods. They are optional and only needed if you want to identify users in your app and associate their actions with a specific user ID.
```ts
type IdentifyFunction = (
userId: string,
traits?: Record<string, AllowedPropertyValues>,
) => void;
export interface AnalyticsProviderClientStrategy {
identify: IdentifyFunction;
reset: () => void;
}
```
To identify users, call the `identify` method, passing the user's ID and an optional traits object:
```tsx
import { identify } from "@turbostarter/analytics-extension";
identify("user-123", { name: "John Doe" });
```
This will associate all future events with the user's ID, allowing you to track user behavior and gain valuable insights into your application's usage patterns.
<Callout title="Configured by default!">
The `identify` method is configured out-of-the-box to react on changes to the user's authentication state.
When the user is authenticated, the `identify` method will be called with the user's ID and the user's traits. When the user is logged out, the `reset` method will be called to clear the existing user identification.
</Callout>
Congratulations! You've now mastered event tracking in your TurboStarter extension. With this knowledge, you're well-equipped to analyze user behaviors and gain valuable insights into your extension's usage patterns. Happy analyzing! 📊

View File

@@ -0,0 +1,197 @@
---
title: Using API client
description: How to use API client to interact with the API.
url: /docs/extension/api/client
---
# Using API client
In browser extension code, you can only access the API client from the **client-side.**
When you create a new component or piece of your extension and want to fetch some data, you can use the API client to do so.
## Creating a client
We're creating a client-side API client in `apps/extension/src/lib/api/index.tsx` file. It's a simple wrapper around the [@tanstack/react-query](https://tanstack.com/query/latest/docs/framework/react/overview) that fetches or mutates data from the API.
It also requires wrapping your views in a `QueryClientProvider` component to provide the API client to the rest of the components.
We recommend to create a separate layout file, which will be used to wrap your pages. TurboStarter comes with a `layout.tsx` file in the `modules/common/layout` folder, which you can use as a template:
```tsx title="layout.tsx"
export const Layout = ({
children,
loadingFallback,
errorFallback,
}: LayoutProps) => {
return (
<ErrorBoundary fallback={errorFallback}>
<Suspense fallback={loadingFallback}>
<QueryClientProvider>{children}</QueryClientProvider>
</Suspense>
</ErrorBoundary>
);
};
```
Remember that every part of your extension will be mounted as a **separate** React component, so you need to wrap each of them in the `QueryClientProvider` component if you want to use the API client inside:
```tsx title="app/popup/main.tsx"
import { Layout } from "~/modules/common/layout/layout";
export default function Popup() {
return <Layout>{/* your popup code here */}</Layout>;
}
```
<Callout type="warn" title="Ensure correct API url">
Inside the `apps/extension/src/lib/api/index.tsx` we're calling a function to get base url of your api, so make sure it's set correctly (especially on production) and your web api endpoint is corresponding with the name there.
```tsx title="index.tsx"
const getBaseUrl = () => {
return env.VITE_SITE_URL;
};
```
As you can see we're mostly relying on the [environment variables](/docs/extension/configuration/environment-variables) to get it, so there shouldn't be any issues with it, but in case, please be aware where to find it 😉
</Callout>
## Queries
Of course, everything comes already configured for you, so you just need to start using `api` in your components/screens.
For example, to fetch the list of posts you can use the `useQuery` hook:
```tsx title="posts.tsx"
import { api } from "~/lib/api";
export const Posts = () => {
const { data: posts, isLoading } = useQuery({
queryKey: ["posts"],
queryFn: async () => {
const response = await api.posts.$get();
if (!response.ok) {
throw new Error("Failed to fetch posts!");
}
return response.json();
},
});
if (isLoading) {
return <p>Loading...</p>;
}
/* do something with the data... */
return (
<div>
<p>{JSON.stringify(posts)}</p>
</div>
);
};
```
It's using the `@tanstack/react-query` [useQuery API](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery), so you shouldn't have any troubles with it.
<Cards>
<Card title="Hono RPC" description="hono.dev" href="https://hono.dev/docs/guides/rpc" />
<Card title="useQuery hook | Tanstack Query" description="tanstack.com" href="https://tanstack.com/query/latest/docs/framework/react/reference/useQuery" />
</Cards>
## Mutations
If you want to perform a mutation in your extension code, you can use the `useMutation` hook that comes straight from the integration with [Tanstack Query](https://tanstack.com/query):
```tsx title="modules/popup/form.tsx"
import { api } from "~/lib/api";
export const CreatePost = () => {
const queryClient = useQueryClient();
const { mutate } = useMutation({
mutationFn: async (post: PostInput) => {
const response = await api.posts.$post(post);
if (!response.ok) {
throw new Error("Failed to create post!");
},
},
onSuccess: () => {
toast.success("Post created successfully!");
queryClient.invalidateQueries({ queryKey: ["posts"] });
},
});
return <form onSubmit={onSubmit(mutate)} />;
};
```
Here, we're also invalidating the query after the mutation is successful. This is a very important step to make sure that the data is updated in the UI.
<Cards>
<Card title="useMutation hook" description="tanstack.com" href="https://tanstack.com/query/latest/docs/framework/react/reference/useMutation" />
<Card title="Query invalidation" description="tanstack.com" href="https://tanstack.com/query/latest/docs/framework/react/guides/query-invalidation" />
</Cards>
## Handling responses
As you can see in the examples above, the [Hono RPC](https://hono.dev/docs/guides/rpc) client returns a plain `Response` object, which you can use to get the data or handle errors. However, implementing this handling in every query or mutation can be tedious and will introduce unnecessary boilerplate in your codebase.
That's why we've developed the `handle` function that unwraps the response for you, handles errors, and returns the data in a consistent format. You can safely use it with any procedure from the API client:
<Tabs items={["Queries", "Mutations"]}>
<Tab value="Queries">
```tsx
// [!code word:handle]
import { handle } from "@turbostarter/api/utils";
import { api } from "~/lib/api";
export const Posts = () => {
const { data: posts, isLoading } = useQuery({
queryKey: ["posts"],
queryFn: handle(api.posts.$get),
});
if (isLoading) {
return <p>Loading...</p>;
}
/* do something with the data... */
return (
<div>
<p>{JSON.stringify(posts)}</p>
</div>
);
};
```
</Tab>
<Tab value="Mutations">
```tsx
// [!code word:handle]
import { handle } from "@turbostarter/api/utils";
import { api } from "~/lib/api/client";
export const CreatePost = () => {
const queryClient = useQueryClient();
const { mutate } = useMutation({
mutationFn: handle(api.posts.$post),
onSuccess: () => {
toast.success("Post created successfully!");
queryClient.invalidateQueries({ queryKey: ["posts"] });
},
});
return <form onSubmit={onSubmit(mutate)} />;
};
```
</Tab>
</Tabs>
With this approach, you can focus on the business logic instead of repeatedly writing code to handle API responses in your browser extension components, making your extension's codebase more readable and maintainable.
The same error handling and response unwrapping benefits apply whether you're building web, mobile, or extension interfaces - allowing you to keep your data fetching logic consistent across all platforms.

View File

@@ -0,0 +1,45 @@
---
title: Overview
description: Get started with the API.
url: /docs/extension/api/overview
---
# Overview
<Callout type="error" title="API deployment required">
To enable communication between your WXT extension and the server in a production environment, the web application with Hono API must be deployed first.
<Cards>
<Card title="API" description="Learn more about the API." href="/docs/web/api/overview" />
<Card title="Web deployment" description="Deploy your web application to production." href="/docs/web/deployment/checklist" />
</Cards>
</Callout>
TurboStarter is designed to be a scalable and production-ready full-stack starter kit. One of its core features is a dedicated and extensible API layer. To enable this in a type-safe manner, we chose [Hono](https://hono.dev) as the API server and client library.
<Callout title="Why Hono?">
Hono is a small, simple, and ultrafast web framework that gives you a way to
define your API endpoints with full type safety. It provides built-in
middleware for common needs like validation, caching, and CORS. It also
includes an [RPC client](https://hono.dev/docs/guides/rpc) for making
type-safe function calls from the frontend. Being edge-first, it's optimized
for serverless environments and offers excellent performance.
</Callout>
All API endpoints and their resolvers are defined in the `packages/api/` package. Here you will find a `modules` folder that contains the different feature modules of the API. Each module has its own folder and exports all its resolvers.
For each module, we create a separate Hono route in the `packages/api/index.ts` file and aggregate all sub-routers into one main router.
The API is then exposed as a route handler that will be provided as a Next.js API route:
```ts title="apps/web/src/app/api/[...route]/route.ts"
import { handle } from "hono/vercel";
import { appRouter } from "@turbostarter/api";
const handler = handle(appRouter);
export { handler as GET, handler as POST };
```
Learn more about how to use the API in your browser extension code in the following sections:

View File

@@ -0,0 +1,46 @@
---
title: Overview
description: Learn how to authenticate users in your extension.
url: /docs/extension/auth/overview
---
# Overview
TurboStarter uses [Better Auth](https://better-auth.com) to handle authentication. It's a secure, production-ready authentication solution that integrates seamlessly with many frameworks and provides enterprise-grade security out of the box.
<Callout title="Why Better Auth?">
One of the core principles of TurboStarter is to do things **as simple as possible**, and to make everything **as performant as possible**.
Better Auth provides an excellent developer experience with minimal configuration required, while maintaining enterprise-grade security standards. Its framework-agnostic approach and focus on performance makes it the perfect choice for TurboStarter.
Recently, Better Auth [announced](https://www.better-auth.com/blog/authjs-joins-better-auth) an incorporation of [Auth.js (27k+ stars on Github)](https://authjs.dev/), making it even more powerful and flexible.
</Callout>
![Better Auth](/images/docs/better-auth.png)
You can read more about Better Auth in the [official documentation](https://better-auth.com/docs).
<Callout type="info" title="IMPORTANT: Shared authentication">
To keep things simple and secure, **the extension shares the same authentication session with your web app.**
This is a common approach used by popular services like [Notion](https://www.notion.so) and [Google Workspace](https://workspace.google.com/). The benefits include:
* Users only need to sign in once through the web app
* The extension automatically inherits the authenticated session
* Sign out actions are synchronized across platforms
* Reduced security surface area and complexity
</Callout>
Before setting up extension authentication, make sure to first [configure authentication for your web app](/docs/web/auth/overview) and then head back to the extension code.
The following sections cover everything you need to know about authentication in your extension:
<Cards>
<Card title="Configuration" description="Configure authentication for your application." href="/docs/web/auth/configuration" />
<Card title="User flow" description="Discover the authentication flow in Turbostarter." href="/docs/web/auth/flow" />
<Card title="OAuth" description="Get started with social authentication." href="/docs/web/auth/oauth" />
<Card title="Session" description="Learn how to manage auth session in your extension." href="/docs/extension/auth/session" />
</Cards>

View File

@@ -0,0 +1,123 @@
---
title: Session
description: Learn how to manage the user session in your extension.
url: /docs/extension/auth/session
---
# Session
We're not implementing fully-featured auth flow in the extension. Instead, **we're sharing the same auth session with the web app.**
It's a common practice in the industry used e.g. by [Notion](https://www.notion.so) and [Google Workspace](https://workspace.google.com/).
That way, when the user is signed in to the web app, the extension can use the same session to authenticate the user, so he doesn't have to sign in again. Also signing out from the extension will affect both platforms.
<Callout title="Remember to add your extension scheme as trusted origin">
For browser extensions, we need to define an [authentication trusted origin](https://www.better-auth.com/docs/reference/security#trusted-origins) using an extension scheme.
Extension schemes (like `chrome-extension://...`) are used for redirecting users to specific screens after authentication and sharing the auth session with the web app.
To find your extension ID, open Chrome and go to `chrome://extensions/`, enable Developer Mode in the top right, and look for your extension's ID. Then add it to your auth server configuration:
```ts title="server.ts"
export const auth = betterAuth({
...
trustedOrigins: ["chrome-extension://your-extension-id"],
...
});
```
Adding your extension scheme to the trusted origins list is crucial for security - it prevents CSRF attacks and blocks malicious open redirects by ensuring only requests from approved origins (your extension) are allowed through.
[Read more about auth security in Better Auth's documentation.](https://www.better-auth.com/docs/reference/security)
</Callout>
## Cookies
When the user signs in to the [web app](/docs/web) through our [Better Auth API](/docs/web/auth/configuration#api), web app is setting the cookie with the session token under your app's domain, which is later used to validate the session on the server.
You can find your cookie in *Cookies* tab in the browser's developer tools (remember to be logged in to the app to check it):
![Session cookie](/images/docs/extension/auth/cookie.png)
To enable your extension to read the cookie and that way share the session with the web app, you need to set the `cookies` permission in the `wxt.config.ts` under `manifest.permissions` field:
```ts title="wxt.config.ts"
export default defineConfig({
manifest: {
permissions: ["cookies"],
},
});
```
And to be able to read the cookie from your app url, you need to set `host_permissions`, which will include your app url:
```ts title="wxt.config.ts"
export default defineConfig({
manifest: {
host_permissions: ["http://localhost/*", "https://your-app-url.com/*"],
},
});
```
Then you would be able to share the cookie with API requests and also read its value using `browser.cookies` API.
<Callout title="Avoid &#x22;<all_urls>&#x22;" type="warn">
Avoid using `<all_urls>` in `host_permissions`. It affects all urls and may cause security issues, as well as a [rejection](https://developer.chrome.com/docs/webstore/review-process#review-time-factors) from the destination store.
</Callout>
<Cards>
<Card title="Declare permissions" href="https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions" description="developer.chrome.com" />
<Card title="chrome.cookies" href="https://developer.chrome.com/docs/extensions/reference/api/cookies" description="developer.chrome.com" />
</Cards>
## Reading session
You **don't** need to worry about reading, parsing, or validating the session cookie. TurboStarter comes with a pre-built solution that ensures your session is correctly shared with the web app.
It also ensures that appropriate cookies are passed to [API](/docs/web/api/overview) requests, so you can safely use [protected endpoints](/docs/web/api/protected-routes) (that require authentication) in your extension.
To get session details in your extension code (e.g., inside a popup window), you can leverage the `useSession` hook provided by the [auth client](https://www.better-auth.com/docs/basic-usage#client-side) (which is also used in the web and mobile apps):
```tsx title="user.tsx"
import { authClient } from "~/lib/auth";
const User = () => {
const {
data: { user, session },
isPending,
} = authClient.useSession();
if (isPending) {
return <p>Loading...</p>;
}
/* do something with the session data... */
return <p>{user?.email}</p>;
};
```
That's how you can access user details right in your extension.
## Signing out
Signing out from the extension also involves using the well-known `signOut` function that is derived from our [auth client](https://www.better-auth.com/docs/basic-usage#signout):
```tsx title="logout.tsx"
import { authClient } from "~/lib/auth";
export const Logout = () => {
return <button onClick={() => authClient.signOut()}>Log out</button>;
};
```
The session is automatically invalidated, so the next use of `useSession` or any other query that depends on the session will return `null`. The UI for both the extension and the web app will be updated to show the user as logged out.
<Callout title="This will sign out the user from the web app as well" type="warn">
As web app is using the same session cookie, the user will be signed out from the web app as well. **This is intentional**, as your extension will most probably serves as an add-on for the web app and it doesn't make sense to keep the user signed in there if the extension is not used.
</Callout>
![Sign out](/images/docs/web/auth/sign-out.png)

View File

@@ -0,0 +1,68 @@
---
title: Billing
description: Get started with billing in TurboStarter.
url: /docs/extension/billing
---
# Billing
As you could guess, there is no sense in implementing the whole billing process inside the browser extension, so we're relying on the [web app](/docs/web/billing/overview) to handle it.
> You probably won't display pricing tables inside a popup window, right?
You can customize the whole flow and onboarding process when a user purchases a plan in your [web app](/docs/web/billing/overview).
Then you would be able to easily fetch customer data to ensure that the user has access to correct extension features.
## Fetching customer data
When your user has purchased a plan from your landing page or web app, you can easily fetch their data using the [API](/docs/extension/api/client).
To do so, just invoke the `getCustomer` query on the `billing` router:
```tsx title="customer-screen.tsx"
import { api } from "~/lib/api";
export default function CustomerScreen() {
const { data: customer, isLoading } = useQuery({
queryKey: ["customer"],
queryFn: handle(api.billing.customer.$get),
});
if (isLoading) return <p>Loading...</p>;
return <p>{customer?.plan}</p>;
}
```
You may also want to ensure that user is logged in before fetching their billing data to avoid unnecessary API calls.
```tsx title="header.tsx"
import { api } from "~/lib/api";
import { authClient } from "~/lib/auth";
export const User = () => {
const {
data: { user },
} = authClient.useSession();
const { data: customer } = useQuery({
queryKey: ["customer"],
queryFn: handle(api.billing.customer.$get),
enabled: !!user, // [!code highlight]
});
if (!user || !customer) {
return null;
}
return (
<div>
<p>{user.email}</p>
<p>{customer.plan}</p>
</div>
);
};
```
Read more about [auth in extension](/docs/extension/auth/overview).

View File

@@ -0,0 +1,92 @@
---
title: CLI
description: Start your new app project with a single command.
url: /docs/extension/cli
---
# CLI
<CliDemo />
To help you get started with TurboStarter **as quickly as possible**, we've developed a [CLI](https://www.npmjs.com/package/@turbostarter/cli) that enables you to create a new project (with all the configuration) in seconds.
The CLI is a set of commands that will help you create a new project, generate code, and manage your project efficiently.
Currently, the following action is available:
* **Starting a new project** - Generate starter code for your project with all necessary configurations in place (billing, database, emails, etc.)
**The CLI is in beta**, and we're actively working on adding more commands and actions. Soon, the following features will be available:
* **Translations** - Translate your project, verify translations, and manage them effectively
* **Installing plugins** - Easily install plugins for your project
* **Dynamic code generation** - Generate dynamic code based on your project structure
## Installation
You can run commands using `npx`:
```bash
npx turbostarter <command>
npx @turbostarter/cli@latest <command>
```
<Callout>
If you don't want to install the CLI globally, you can simply replace the examples below with `npx @turbostarter/cli@latest` instead of `turbostarter`.
This also allows you to always run the latest version of the CLI without having to update it.
</Callout>
## Usage
Running the CLI without any arguments will display the general information about the CLI:
```bash
Usage: turbostarter [options] [command]
Your Turbo Assistant for starting new projects, adding plugins and more.
Options:
-v, --version display the version number
-h, --help display help for command
Commands:
new create a new TurboStarter project
help [command] display help for command
```
You can also display help for it or check the actual version.
### Starting a new project
Use the `new` command to initialize configuration and dependencies for a new project.
```bash
npx turbostarter new
```
You will be asked a few questions to configure your project:
```bash
✔ All prerequisites are satisfied, let's start! 🚀
? What do you want to ship?
◉ Web app
◉ Mobile app
◯ Browser extension
? Enter your project name.
? How do you want to use database?
Local (powered by Docker)
Cloud
? What do you want to use for billing?
Stripe
Lemon Squeezy
...
🎉 You can now get started. Open the project and just ship it! 🎉
Problems? https://www.turbostarter.dev/docs
```
It will create a new project, configure providers, install dependencies and start required services in development mode.

View File

@@ -0,0 +1,63 @@
---
title: App configuration
description: Learn how to setup the overall settings of your extension.
url: /docs/extension/configuration/app
---
# App configuration
The application configuration is set at `apps/extension/src/config/app.ts`. This configuration stores some overall variables for your application.
This allows you to host multiple apps in the same monorepo, as every application defines its own configuration.
The recommendation is to **not update this directly** - instead, please define the environment variables and override the default behavior. The configuration is strongly typed so you can use it safely accross your codebase - it'll be validated at build time.
```ts title="apps/extension/src/config/app.ts"
import env from "env.config";
export const appConfig = {
name: env.VITE_PRODUCT_NAME,
url: env.VITE_SITE_URL,
locale: env.VITE_DEFAULT_LOCALE,
theme: {
mode: env.VITE_THEME_MODE,
color: env.VITE_THEME_COLOR,
},
} as const;
```
For example, to set the extension default theme color, you'd update the following variable:
```dotenv title=".env.local"
VITE_THEME_COLOR="yellow"
```
<Callout type="warn" title="Do NOT use process.env!">
Do NOT use `process.env` to get the values of the variables. Variables
accessed this way are not validated at build time, and thus the wrong variable
can be used in production.
</Callout>
## WXT config
To configure framework-specific settings, you can use the `wxt.config.ts` file. You can configure a lot of options there, such as [manifest](/docs/extension/configuration/manifest), [project structure](https://wxt.dev/guide/essentials/project-structure.html) or even [underlying Vite config](https://wxt.dev/guide/essentials/config/vite.html):
```ts title="wxt.config.ts"
import { defineConfig } from "wxt";
export default defineConfig({
srcDir: "src",
entrypointsDir: "app",
outDir: "build",
modules: [],
manifest: {
// Put manifest changes here
},
vite: () => ({
// Override config here, same as `defineConfig({ ... })`
// inside vite.config.ts files
}),
});
```
Make sure to setup it correctly, as it's the main source of config for your development, build and publishing process.

View File

@@ -0,0 +1,123 @@
---
title: Environment variables
description: Learn how to configure environment variables.
url: /docs/extension/configuration/environment-variables
---
# Environment variables
Environment variables are defined in the `.env` file in the root of the repository and in the root of the `apps/extension` package.
* **Shared environment variables**: Defined in the **root** `.env` file. These are shared between environments (e.g., development, staging, production) and apps (e.g., web, extension).
* **Environment-specific variables**: Defined in `.env.development` and `.env.production` files. These are specific to the development and production environments.
* **App-specific variables**: Defined in the app-specific directory (e.g., `apps/extension`). These are specific to the app and are not shared between apps.
* **Bundle-specific variables**: Specific to the [bundle target](https://wxt.dev/guide/essentials/config/environment-variables.html#built-in-environment-variables) (e.g. `.env.safari`, `.env.firefox`) or [bundle tag](https://wxt.dev/guide/essentials/config/environment-variables.html#built-in-environment-variables) (e.g. `.env.testing`)
* **Build environment variables**: Not stored in the `.env` file. Instead, they are stored in the environment variables of the CI/CD system.
* **Secret keys**: They're not stored on the extension side, instead [they're defined on the web side.](/docs/web/configuration/environment-variables#secret-keys)
## Shared variables
Here you can add all the environment variables that are shared across all the apps.
To override these variables in a specific environment, please add them to the specific environment file (e.g. `.env.development`, `.env.production`).
```dotenv title=".env.local"
# Shared environment variables
# The database URL is used to connect to your database.
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"
# The name of the product. This is used in various places across the apps.
PRODUCT_NAME="TurboStarter"
# The url of the web app. Used mostly to link between apps.
URL="http://localhost:3000"
...
```
## App-specific variables
Here you can add all the environment variables that are specific to the app (e.g. `apps/extension`).
You can also override the shared variables defined in the root `.env` file.
```dotenv title="apps/extension/.env.local"
# App-specific environment variables
# Env variables extracted from shared to be exposed to the client in WXT (Vite) extension
VITE_SITE_URL="${URL}"
VITE_DEFAULT_LOCALE="${DEFAULT_LOCALE}"
# Theme mode and color
VITE_THEME_MODE="system"
VITE_THEME_COLOR="orange"
...
```
<Callout title="VITE_ prefix">
To make environment variables available in the browser extension code, you need to prefix them with `VITE_`. They will be injected to the code during the build process.
Only environment variables prefixed with `VITE_` will be injected.
[Read more about Vite environment variables.](https://vite.dev/guide/env-and-mode.html#env-files)
</Callout>
## Bundle-specific variables
WXT also provides environment variables specific to a certain [build target](https://wxt.dev/guide/essentials/config/environment-variables.html#built-in-environment-variables) or [build tag](https://wxt.dev/guide/essentials/config/environment-variables.html#built-in-environment-variables) when creating the final bundle. Given the following build command:
```json title="package.json"
"scripts": {
"build": "wxt build -b firefox --mode testing"
}
```
The following env files will be considered, ordered by priority:
* `.env.firefox`
* `.env.testing`
* `.env`
You shouldn't worry much about this, as TurboStarter comes with already configured build processes for all the major browsers.
## Build environment variables
To allow your extension to build properly on CI you need to define your environment variables on your CI/CD system (e.g. [Github Actions](https://docs.github.com/en/actions/learn-github-actions/environment-variables)).
TurboStarter comes with predefined Github Actions workflow used to build and submit your extension to the stores. It's located in `.github/workflows/publish-extension.yml` file.
To correctly set up the build environment variables, you need to define them under `env` section and then add them as a [secrets](http://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions) to your repository.
```yaml title="publish-extension.yml"
...
jobs:
extension:
name: 🚀 Publish extension
runs-on: ubuntu-latest
environment: Production
env:
VITE_SITE_URL: ${{ secrets.SITE_URL }}
...
```
We'll go through the whole process of building and publishing the extension in the [publishing guide](/docs/extension/publishing/checklist).
## Secret keys
Secret keys and sensitive information are to be **never** stored on the extension app code.
<Callout title="What does this mean?">
It means that you will need to add the secret keys to the **web app, where the API is deployed.**
The browser extension should only communicate with the backend API, which is typically part of the web app. The web app is responsible for handling sensitive operations and storing secret keys securely.
[See web documentation for more details.](/docs/web/configuration/environment-variables#secret-keys)
This is not a TurboStarter-specific requirement, but a best practice for security for any
application. Ultimately, it's your choice.
</Callout>

View File

@@ -0,0 +1,111 @@
---
title: Manifest
description: Learn how to configure the manifest of your extension.
url: /docs/extension/configuration/manifest
---
# Manifest
As a requirement from web stores, every extension must have a `manifest.json` file in its root directory that lists important information about the structure and behavior of that extension.
It's a JSON file that contains metadata about the extension, such as its name, version, and permissions.
You can read more about it in the [official documentation](https://developer.chrome.com/docs/extensions/reference/manifest).
## Where is the `manifest.json` file?
WXT **abstracts away** the manifest file. The framework generates the manifest under the hood based on your source files and configurations you export from your code, similar to how Next.js abstracts page routing and SSG with the file system and page components.
That way, you don't have to manually create the `manifest.json` file and worry about correctly setting all the fields.
Most of the common properties are taken from the `package.json` and `wxt.config.ts` files:
| Manifest Field | Abstractions |
| ------------------------ | ------------------------------------------------------------- |
| icons | Auto generated with the `icon.png` in the `/assets` directory |
| action, browser\_actions | Popup window |
| options\_ui | Options page |
| content\_scripts | Content scripts |
| background | Background service worker |
| version | set by the `version` field in `package.json` |
| name | set by the `name` field in `wxt.config.ts` |
| description | set by the `description` field in `wxt.config.ts` |
| author | set by the `author` field in `wxt.config.ts` |
| homepage\_url | set by the `homepage` field in `wxt.config.ts` |
WXT build process centralizes common metadata and resolves any static file references (such as popup, background, content scripts, and so on) automatically.
This enables you to focus on the metadata that matters, such as name, description, OAuth, and so on.
## Overriding manifest
Sometimes, you want to override the default manifest fields (e.g. because you need to add a new permission that is required for your extension to work).
You'll need to modify your project's `wxt.config.ts` like so:
```ts title="apps/extension/wxt.config.ts"
export default defineConfig({
manifest: {
permissions: ["activeTab"],
},
});
```
Then, your settings will be merged with the settings auto-generated by WXT.
### Environment variables
You can use environment variables inside the manifest overrides:
```ts title="apps/extension/wxt.config.ts"
export default defineConfig({
manifest: {
browser_specific_settings: {
gecko: {
id: import.meta.env.VITE_FIREFOX_EXT_ID,
},
},
},
});
```
If the environment variable could not be found, the field will be removed completely from the manifest.
### Locales
TurboStarter extension supports [extension localization](https://developer.chrome.com/docs/extensions/reference/api/i18n) out-of-the-box. You can customize e.g. your extension's name and description based on the language of the user's browser.
Locales are defined in the `/public/_locales` directory. The directory should contain a `messages.json` file for each language you want to support (e.g. `/public/_locales/en/messages.json` and `/public/_locales/es/messages.json`).
By default, the first locale alphabetically available is used as default. However, you can specify a `default_locale` in your manifest like so:
```ts title="apps/extension/wxt.config.ts"
export default defineConfig({
manifest: {
default_locale: "en",
},
});
```
To reference a locale string inside your manifest overrides, wrap the key inside `__MSG_<key>__`:
```ts title="apps/extension/wxt.config.ts"
export default defineConfig({
manifest: {
name: "__MSG_extensionName__",
description: "__MSG_extensionDescription__",
},
});
```
Apart of this, we also configure [in-extension internationalization](/docs/extension/internationalization) out-of-the-box to easily translate your components and views.
<Cards>
<Card title="Manifest file format" href="https://developer.chrome.com/docs/extensions/reference/manifest" description="developer.chrome.com" />
<Card title="WXT Manifest" href="https://wxt.dev/guide/essentials/config/manifest.html" description="wxt.dev" />
<Card title="chrome.i18n" href="https://developer.chrome.com/docs/extensions/reference/api/i18n" description="developer.chrome.com" />
<Card title="WXT i18n" href="https://wxt.dev/guide/essentials/i18n.html" description="wxt.dev" />
</Cards>

View File

@@ -0,0 +1,83 @@
---
title: Adding apps
description: Learn how to add apps to your Turborepo workspace.
url: /docs/extension/customization/add-app
---
# Adding apps
<Callout title="Advanced topic" type="warn">
This is an **advanced topic** - you should only follow these instructions if you are sure you want to add a new app to your TurboStarter project within your monorepo and want to keep pulling updates from the TurboStarter repository.
</Callout>
In some ways - creating a new repository may be the easiest way to manage your application. However, if you want to keep your application within the monorepo and pull updates from the TurboStarter repository, you can follow these instructions.
To pull updates into a separate application outside of `extension` - we can use [git subtree](https://www.atlassian.com/git/tutorials/git-subtree).
Basically, we will create a subtree at `apps/extension` and create a new remote branch for the subtree. When we create a new application, we will pull the subtree into the new application. This allows us to keep it in sync with the `apps/extension` folder.
To add a new app to your TurboStarter project, you need to follow these steps:
<Steps>
<Step>
## Create a subtree
First, we need to create a subtree for the `apps/extension` folder. We will create a branch named `extension-branch` and create a subtree for the `apps/extension` folder.
```bash
git subtree split --prefix=apps/extension --branch extension-branch
```
</Step>
<Step>
## Create a new app
Now, we can create a new application in the `apps` folder.
Let's say we want to create a new app `ai-chat` at `apps/ai-chat` with the same structure as the `apps/extension` folder (which acts as the template for all new apps).
```bash
git subtree add --prefix=apps/ai-chat origin extension-branch --squash
```
You should now be able to see the `apps/ai-chat` folder with the contents of the `apps/extension` folder.
</Step>
<Step>
## Update the app
When you want to update the new application, follow these steps:
### Pull the latest updates from the TurboStarter repository
The command below will update all the changes from the TurboStarter repository:
```bash
git pull upstream main
```
### Push the `extension-branch` updates
After you have pulled the updates from the TurboStarter repository, you can split the branch again and push the updates to the extension-branch:
```bash
git subtree split --prefix=apps/extension --branch extension-branch
```
Now, you can push the updates to the `extension-branch`:
```bash
git push origin extension-branch
```
### Pull the updates to the new application
Now, you can pull the updates to the new application:
```bash
git subtree pull --prefix=apps/ai-chat origin extension-branch --squash
```
</Step>
</Steps>
That's it! You now have a new application in the monorepo 🎉

View File

@@ -0,0 +1,100 @@
---
title: Adding packages
description: Learn how to add packages to your Turborepo workspace.
url: /docs/extension/customization/add-package
---
# Adding packages
<Callout title="Advanced topic" type="warn">
This is an **advanced topic** - you should only follow these instructions if you are sure you want to add a new package to your TurboStarter application instead of adding a folder to your application in `apps/web` or modify existing packages under `packages`. You don't need to do this to add a new page or component to your application.
</Callout>
To add a new package to your TurboStarter application, you need to follow these steps:
<Steps>
<Step>
## Generate a new package
First, enter the command below to create a new package in your TurboStarter application:
```bash
turbo gen package
```
Turborepo will ask you to enter the name of the package you want to create. Enter the name of the package you want to create and press enter.
If you don't want to add dependencies to your package, you can skip this step by pressing enter.
The command will have generated a new package under packages named `@turbostarter/<package-name>`. If you named it `example`, the package will be named `@turbostarter/example`.
</Step>
<Step>
## Export a module from your package
By default, the package exports a single module using the `index.ts` file. You can add more exports by creating new files in the package directory and exporting them from the `index.ts` file or creating export files in the package directory and adding them to the `exports` field in the `package.json` file.
### From `index.ts` file
The easiest way to export a module from a package is to create a new file in the package directory and export it from the `index.ts` file.
```ts title="packages/example/src/module.ts"
export function example() {
return "example";
}
```
Then, export the module from the `index.ts` file.
```ts title="packages/example/src/index.ts"
export * from "./module";
```
### From `exports` field in `package.json`
**This can be very useful for tree-shaking.** Assuming you have a file named `module.ts` in the package directory, you can export it by adding it to the `exports` field in the `package.json` file.
```json title="packages/example/package.json"
{
"exports": {
".": "./src/index.ts",
"./module": "./src/module.ts"
}
}
```
**When to do this?**
1. when exporting two modules that don't share dependencies to ensure better tree-shaking. For example, if your exports contains both client and server modules.
2. for better organization of your package
For example, create two exports `client` and `server` in the package directory and add them to the `exports` field in the `package.json` file.
```json title="packages/example/package.json"
{
"exports": {
".": "./src/index.ts",
"./client": "./src/client.ts",
"./server": "./src/server.ts"
}
}
```
1. The `client` module can be imported using `import { client } from '@turbostarter/example/client'`
2. The `server` module can be imported using `import { server } from '@turbostarter/example/server'`
</Step>
<Step>
## Use the package in your extension
You can now use the package in your extension by importing it using the package name:
```ts title="app/popup/index.tsx"
import { example } from "@turbostarter/example";
console.log(example());
```
</Step>
</Steps>
Et voilà! You have successfully added a new package to your TurboStarter extension. 🎉

View File

@@ -0,0 +1,120 @@
---
title: Components
description: Manage and customize your extension components.
url: /docs/extension/customization/components
---
# Components
For the components part, we're using [shadcn/ui](https://ui.shadcn.com) for atomic, accessible and highly customizable components.
<Callout type="info" title="Why shadcn/ui?">
shadcn/ui is a powerful tool that allows you to generate pre-designed
components with a single command. It's built with Tailwind CSS and Radix UI,
and it's highly customizable.
</Callout>
TurboStarter defines two packages that are responsible for the UI part of your app:
* `@turbostarter/ui` - shared styles, [themes](/docs/web/customization/styling#themes) and assets (e.g. icons)
* `@turbostarter/ui-web` - pre-built UI web components, ready to use in your app
## Adding a new component
There are basically two ways to add a new component:
<Tabs items={["Using the CLI", "Copy-pasting"]}>
<Tab value="Using the CLI">
TurboStarter is fully compatible with [shadcn CLI](https://ui.shadcn.com/docs/cli), so you can generate new components with single command.
Run the following command from the **root** of your project:
```bash
pnpm --filter @turbostarter/ui-web ui:add
```
This will launch an interactive command-line interface to guide you through the process of adding a new component where you can pick which component you want to add.
```bash
Which components would you like to add? > Space to select. A to toggle all.
Enter to submit.
◯ accordion
◯ alert
◯ alert-dialog
◯ aspect-ratio
◯ avatar
◯ badge
◯ button
◯ calendar
◯ card
◯ checkbox
```
Newly created components will appear in the `packages/ui/web/src` directory.
</Tab>
<Tab value="Copy-pasting">
You can always copy-paste a component from the [shadcn/ui](https://ui.shadcn.com/docs/components) website and modify it to your needs.
This is possible, because the components are headless and don't need (in most cases) any additional dependencies.
Copy code from the website, create a new file in the `packages/ui/web/src` directory and paste the code into the file.
</Tab>
</Tabs>
<Callout title="Keep it atomic" type="warn">
Keep in mind that you should always try to keep shared components as atomic as possible. This will make it easier to reuse them and to build specific views by composition.
E.g. include components like `Button`, `Input`, `Card`, `Dialog` in shared package, but keep specific components like `LoginForm` in your app directory.
</Callout>
## Using components
Each component is a standalone entity which has a separate export from the package. It helps to keep things modular, avoid unnecessary dependencies and make tree-shaking possible.
To import a component from the UI package, use the following syntax:
```tsx title="components/my-component.tsx"
// [!code word:card]
import {
Card,
CardContent,
CardHeader,
CardFooter,
CardTitle,
CardDescription,
} from "@turbostarter/ui-web/card";
```
Then you can use it to build a component specific to your app:
```tsx title="components/my-component.tsx"
export function MyComponent() {
return (
<Card>
<CardHeader>
<CardTitle>My Component</CardTitle>
</CardHeader>
<CardContent>
<p>My Component Content</p>
</CardContent>
<CardFooter>
<Button>Click me</Button>
</CardFooter>
</Card>
);
}
```
<Callout title="Recommendation: use v0 to generate layouts">
We recommend using [v0](https://v0.dev) to generate layouts for your app. It's a powerful tool that allows you to generate layouts from the natural language instructions.
Of course, **it won't replace a designer**, but it can be a good starting point for your layout.
</Callout>
<Cards>
<Card href="https://ui.shadcn.com/" title="shadcn/ui" description="ui.shadcn.com" />
<Card href="https://v0.dev/chat" title="v0 by Vercel" description="v0.dev" />
</Cards>

View File

@@ -0,0 +1,159 @@
---
title: Styling
description: Get started with styling your extension.
url: /docs/extension/customization/styling
---
# Styling
To build the extension interface TurboStarter comes with [Tailwind CSS](https://tailwindcss.com/) and [Radix UI](https://www.radix-ui.com/) pre-configured.
<Callout title="Why Tailwind CSS and Radix UI?" type="info">
The combination of Tailwind CSS and Radix UI gives ready-to-use, accessible UI components that can be fully customized to match your brands design.
</Callout>
## Tailwind configuration
In the `packages/ui/shared/src/styles` directory, you will find shared CSS files with Tailwind CSS configuration. To change global styles, you can edit the files in this folder.
Here is an example of a shared CSS file that includes the Tailwind CSS configuration:
```css title="packages/ui/shared/src/styles/globals.css"
@import "tailwindcss";
@import "./themes.css";
@custom-variant dark (&:is(.dark *));
:root {
--radius: 0.65rem;
}
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
...
}
```
For colors, we rely strictly on [CSS Variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) in [OKLCH](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch) format to allow for easy theme management without the need for any JavaScript.
Also, each app has its own `globals.css` file, which extends the shared config and allows you to override the global styles.
Here is an example of an extension's `globals.css` file:
```css title="apps/extension/src/assets/styles/globals.css"
@import url("https://fonts.googleapis.com/css2?family=Geist+Mono:wght@100..900&family=Geist:wght@100..900&display=swap");
@import "@turbostarter/ui/globals.css";
@import "@turbostarter/ui-web/globals.css";
@theme {
--font-sans: "Geist", sans-serif;
--font-mono: "Geist Mono", monospace;
}
```
This way, we maintain a separation of concerns and a clear structure for the Tailwind CSS configuration.
## Themes
TurboStarter comes with **9+** predefined themes, which you can use to quickly change the look and feel of your app.
They're defined in the `packages/ui/shared/src/styles/themes` directory. Each theme is a set of variables that can be overridden:
```ts title="packages/ui/shared/src/styles/themes/colors/orange.ts"
export const orange = {
light: {
background: [1, 0, 0],
foreground: [0.141, 0.005, 285.823],
card: [1, 0, 0],
"card-foreground": [0.141, 0.005, 285.823],
...
}
} satisfies ThemeColors;
```
Each variable is stored as a [OKLCH](https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/oklch) array, which is then converted to a CSS variable at build time (by our custom build script). That way we can ensure full type-safety and reuse themes across different parts of our apps (e.g. use the same theme in emails).
Feel free to add your own themes or override the existing ones to match your brand's identity.
To apply a theme to your app, you can use the `data-theme` attribute on your layout wrapper for each part of the extension:
```tsx title="modules/common/layout/layout.tsx"
import { StorageKey, useStorage } from "~/lib/storage";
export const Layout = ({ children }: { children: React.ReactNode }) => {
const { data } = useStorage(StorageKey.THEME);
return (
<div id="main" data-theme={data.color}>
{children}
</div>
);
};
```
In TurboStarter, we're using [Storage API](/docs/extension/structure/storage) to persist the user's theme selection and then apply it to the `div#main` element.
## Dark mode
The starter kit comes with a built-in dark mode support.
Each theme has a corresponding dark mode variables which are used to change the theme to its dark mode counterpart.
```ts title="packages/ui/shared/src/styles/themes/colors/orange.ts"
export const orange = {
light: {},
dark: {
background: [0.141, 0.005, 285.823],
foreground: [0.985, 0, 0],
card: [0.21, 0.006, 285.885],
"card-foreground": [0.985, 0, 0],
...
}
} satisfies ThemeColors;
```
Because the dark variant is defined to use a class (`@custom-variant dark (&:is(.dark *))`) in the shared Tailwind configuration, we need to add the `dark` class to the root element to apply dark mode styles.
The same as for the theme color, we're using here the [Storage API](/docs/extension/structure/storage) to persist the user's dark mode selection and then apply correct class name to the root `div` element:
```tsx title="modules/common/layout/layout.tsx"
import { StorageKey, useStorage } from "~/lib/storage";
export const Layout = ({ children }: { children: React.ReactNode }) => {
const { data } = useStorage(StorageKey.THEME);
return (
<div
id="root"
className={cn({
dark:
data.mode === THEME_MODE.DARK ||
(data.mode === THEME_MODE.SYSTEM &&
window.matchMedia("(prefers-color-scheme: dark)").matches),
})}
>
{children}
</div>
);
};
```
You can also define the default theme mode and color in the [app configuration](/docs/extension/configuration/app).
<Cards>
<Card title="Tailwind CSS" description="tailwindcss.com" href="https://tailwindcss.com/" />
<Card title="Radix UI" description="radix-ui.com" href="https://www.radix-ui.com/" />
</Cards>

View File

@@ -0,0 +1,39 @@
---
title: Database
description: Get started with the database.
url: /docs/extension/database
---
# Database
<Callout type="error" title="API deployment required">
To enable communication between your WXT extension and the server in a production environment, the web application with Hono API must be deployed first.
<Cards>
<Card title="API" description="Learn more about the API." href="/docs/web/api/overview" />
<Card title="Web deployment" description="Deploy your web application to production." href="/docs/web/deployment/checklist" />
</Cards>
</Callout>
As browser extensions use only client-side code, **there's no way to interact with the database directly**.
Also, you should avoid any workarounds to interact with the database directly, because it can lead to leaking your database credentials and other security issues.
## Recommended approach
You can safely use the [API](/docs/extension/api/overview) and invoke procedures which will run queries on the database.
To do this you need to set up the database on the [web, server side](/docs/web/database/overview) and then use the [API client](/docs/extension/api/client) to interact with it.
Learn more about its configuration in the web part of the docs, especially in the following sections:
<Cards>
<Card title="Overview" description="Get started with the database" href="/docs/web/database/overview" />
<Card title="Schema" description="Learn about the database schema." href="/docs/web/database/schema" />
<Card title="Migrations" description="Migrate your changes to the database." href="/docs/web/database/migrations" />
<Card title="Database client" description="Use database client to interact with the database." href="/docs/web/database/client" />
</Cards>

View File

@@ -0,0 +1,66 @@
---
title: Extras
description: See what you get together with the code.
url: /docs/extension/extras
---
# Extras
## Tips and Tricks
In many places, next to the code you will find some marketing tips, design suggestions, and potential risks. This is to help you build a better product and avoid common pitfalls.
```tsx title="Hero.tsx"
return (
<header>
{/* 💡 Use something that user can visualize e.g.
"Ship your startup while on the toilet" */}
<h1>Best startup on the world</h1>
</header>
);
```
### Submission tips
When it comes to mobile app and browser extension, you must submit your product to review from Apple/Google etc. We have some tips for you to make sure your submission goes smoothly.
```json title="app.json"
{
"ios": {
"infoPlist": {
/* 🍎 add descriptive justification of using this permission on iOS */
"NSCameraUsageDescription": "This app uses the camera to scan barcodes on event tickets."
}
}
}
```
As well as providing you with the info on how to make your store listings better:
```json title="package.json"
{
"manifest": {
/* 💡 Use localized messages to get more visibility in web stores */
"name": "__MSG_extensionName__",
"default_locale": "en"
}
}
```
## Discord community
We have a Discord community where you can ask questions and share your projects. It's a great place to get help and meet other developers. Check more details at [/discord](/discord).
<DiscordCta source="extras" />
![Discord](/images/docs/discord.png)
## 25+ SaaS Ideas
Not sure what to build? We have a list of **25+** SaaS ideas that you can use to get started 🔥
Grouped by category, these ideas are a great way to get inspired and start building your next project.
Including design, copies, marketing tips and potential risks, this list is a great resource for anyone looking to build a SaaS product.
![SaaS Ideas](/images/docs/saas-ideas.png)

View File

@@ -0,0 +1,92 @@
---
title: FAQ
description: Find answers to common technical questions.
url: /docs/extension/faq
---
# FAQ
## Why isn't everything hidden and configured with one BIG config file?
TurboStarter intentionally exposes the underlying code rather than hiding it behind configuration files (like some starters do). This design choice follows our **you own your code** philosophy, giving you full control and flexibility over your codebase.
While a single config file might seem simpler initially, it often becomes restrictive when you need to customize functionality beyond what the config allows. With direct access to the code, you can modify any part of the system to match your specific requirements.
## I don't know some technology! Should I buy TurboStarter?
You should be prepared for a learning curve or consider learning it first. However, TurboStarter will still work for you if you're willing to learn.
Even without knowing some technologies, you can still use the rest of the features.
## I don't need mobile app or browser extension, what should I do?
You can simply ignore the mobile app and browser extension parts of the project. You can remove the `apps/mobile` and `apps/extension` directories from the project.
The modular nature of TurboStarter allows you to remove parts of the project that you don't need without affecting the rest of the stack.
## I want to use a different provider for X
Sure! TurboStarter is designed to be modular, so configuring new provider (e.g. for emails, billing or any other service) is straightforward. You just need to make sure your configuration is compatible with common interface to be able to plug it into the codebase.
## Will you add more packages in the future?
Yes, we will keep updating TurboStarter with new packages and features. This kit is designed to be modular, allowing for new features and packages to be added without interfering with your existing code. You can always [update your project](/docs/web/installation/update) to the latest version.
## Can I use this kit for a non-SaaS project?
This kit is mainly designed for SaaS projects. If you're building something other than a SaaS, the Next.js SaaS Boilerplate might include features you don't need. You can still use it for non-SaaS projects, but you may need to remove or modify features that are specific to SaaS use cases.
## Can I use personal accounts only?
Yes! You can disable team accounts and have personal accounts only by setting a feature flag.
## Does it set up the production instance for me?
No, TurboStarter does not set up the production instance for you. This includes setting up databases, Stripe, or any other services you need. TurboStarter does not have access to your Stripe or Resend accounts, so setup on your end is required. TurboStarter provides the codebase and documentation to help you set up your SaaS project.
## Does the starter include Solito?
No. Solito will not be included in this repo. It is a great tool if you want to share code between your Next.js and Expo app. However, the main purpose of this repo is not the integration between Next.js and Expo — it's the code splitting of your SaaS platforms into a monorepo. You can utilize the monorepo with multiple apps, and it can be any app such as Vite, Electron, etc.
Integrating Solito into this repo isn't hard, and there are a few [official templates](https://github.com/nandorojo/solito/tree/master/example-monorepos) by the creators of Solito that you can use as a reference.
## Does this pattern leak backend code to my client applications?
No, it does not. The `api` package should only be a production dependency in the Next.js application where it's served. The Expo app, browser extension, and all other apps you may add in the future should only add the `api` package as a dev dependency. This lets you have full type safety in your client applications while keeping your backend code safe.
If you need to share runtime code between the client and server, you can create a separate `shared` package for this and import it on both sides.
## How do I get support if I encounter issues?
For support, you can:
1. Visit our [Discord](https://discord.gg/KjpK2uk3JP)
2. Contact us via support email ([hello@turbostarter.dev](mailto:hello@turbostarter.dev))
## Are there any example projects or demos?
Yes - feel free to check out our demo app at [demo.turbostarter.dev](https://demo.turbostarter.dev). Also, you can get inspired by projects built by our customers - take a look at [Showcase](/#showcase).
## How do I deploy my application?
Please check the [production checklist](/docs/web/deployment/checklist) for more information.
## How do I update my project when a new version of the boilerplate is released?
Please read the [documentation for updating your TurboStarter code](/docs/web/installation/update).
## Can I use the React package X with this kit?
Yes, you can use any React package with this kit. The kit is based on React, so you are generally only constrained by the underlying technologies and not by the kit itself. Since you own and can edit all the code, you can adapt the kit to your needs. However, if there are limitations with the underlying technology, you might need to work around them.
## Can I integrate TurboStarter into an existing project?
TurboStarter is a full-stack starter intended to be used as the foundation of your app. You can copy individual modules or patterns into an existing codebase, but retrofitting the entire starter into a mature project is typically not recommended and is not officially supported. If you choose to copy parts, prefer isolating boundaries (e.g., `packages/` modules) and aligning interfaces first.
## Where can I deploy my application?
TurboStarter targets modern Node.js/Next.js runtimes. You can deploy to providers that support these environments, such as [Vercel](/docs/web/deployment/vercel), [Railway](/docs/web/deployment/railway), [Render](/docs/web/deployment/render), [Fly](/docs/web/deployment/fly), or [Netlify](/docs/web/deployment/netlify) - following their Next.js guidance. Review our [production checklist](/docs/web/deployment/checklist) before going live.
## Can I easily swap providers (billing, email, etc.)?
Yes. The starter organizes integrations behind clear interfaces so you can replace providers (e.g., billing or email) with minimal surface changes. Keep your implementation behind a module boundary and adapt to the existing types to avoid ripple effects.

View File

@@ -0,0 +1,336 @@
---
title: Introduction
description: Get started with TurboStarter extension kit.
url: /docs/extension
---
# Introduction
Welcome to the TurboStarter documentation. This is your starting point for learning about the starter kit, its structure, features, and how to use it for your app development.
<ThemedImage light="/images/docs/demo/light.webp" dark="/images/docs/demo/dark.webp" alt="TurboStarter demo" width={2311} height={1562} zoomable priority />
## What is TurboStarter?
TurboStarter is a fullstack starter kit that helps you build scalable and production-ready web apps, mobile apps, and browser extensions in minutes.
Looking to bootstrap your project quickly? Check out our [TurboStarter CLI guide](/blog/the-only-turbo-cli-you-need-to-start-your-next-project-in-seconds) to get started in seconds.
## Demo apps
TurboStarter provides a suite of live demo applications you can try instantly - right in your browser, on your phone, or via browser extensions. Try them live by clicking the buttons below.
<DemoBadges
urls={{
android:
"https://play.google.com/store/apps/details?id=com.turbostarter.core",
ios: "https://apps.apple.com/app/id6754278899",
chrome:
"https://chromewebstore.google.com/detail/bcjmonmlfbnngpkllpnpmnjajaciaboo",
firefox: "https://addons.mozilla.org/addon/turbostarter_",
edge: "https://microsoftedge.microsoft.com/addons/detail/turbostarter/ianbflanmmoeleokihabnmmcahhfijig",
web: "https://demo.turbostarter.dev",
}}
/>
## Principles
TurboStarter is built with the following principles:
* **As simple as possible** - It should be easy to understand, easy to use, and strongly avoid overengineering things.
* **As few dependencies as possible** - It should have as few dependencies as possible to allow you to take full control over every part of the project.
* **As performant as possible** - It should be fast and light without any unnecessary overhead.
## Features
Before diving into the technical details, let's overview the features TurboStarter provides.
### Multi-platform development
* [Web](/docs/web/stack): Build web apps with React, Next.js, and Tailwind CSS.
* [Mobile](/docs/mobile/stack): Build mobile apps with React Native and Expo.
* [Browser extension](/docs/extension/stack): Build browser extensions with React and WXT.
For those interested in AI development, check out our dedicated [TurboStarter AI documentation](/ai/docs) with specialized features for building AI-powered applications.
<Callout title="Available. Everywhere.">
Most features are available on all platforms. You can use the **same codebase** to build web, mobile, and browser extension apps.
</Callout>
### Authentication
<Cards>
<Card title="Ready-to-use components and views" description="Pre-built authentication components and pages that match your brand's unique style." className="shadow-none" />
<Card title="Email/password authentication" description="Traditional email and password auth implementing validation and security best practices." className="shadow-none" />
<Card title="Magic links" description="Passwordless authentication through secure email-based magic links, including rate limiting." className="shadow-none" />
<Card title="Password recovery" description="Complete password reset flow including email verification and secure token handling." className="shadow-none" />
<Card title="Multi-factor authentication (MFA)" description="Increase account security with support for 2FA (authenticator apps, TOTP), ready to use and customizable in your app." className="shadow-none" />
<Card title="Passkeys (passwordless)" description="Passwordless authentication using Passkeys (FIDO2/WebAuthn) for seamless, phishing-resistant sign-ins." className="shadow-none" />
<Card title="Anonymous" description="Allow users to proceed anonymously without requiring authentication." className="shadow-none" />
<Card title="OAuth providers" description="Pre-configured social authentication for Google and GitHub, ready to use." className="shadow-none" />
</Cards>
### Organizations/teams
<Cards>
<Card title="Multi-tenancy" description="Multi-tenant organization model with ownership and membership." className="shadow-none" />
<Card title="Teams and members" description="Create teams, invite members, assign roles and manage seats." className="shadow-none" />
<Card title="Invitations" description="Email-based invites with role presets and expiry." className="shadow-none" />
<Card title="Roles per organization" description="Role-based permissions scoped to each organization." className="shadow-none" />
</Cards>
### Billing
<Cards>
<Card title="Subscriptions" description="Recurring billing system supporting multiple plans, pricing tiers and usage metrics." className="shadow-none" />
<Card title="One-time payments" description="Simple payment processing featuring secure checkout and payment confirmation." className="shadow-none" />
<Card title="Webhooks" description="Real-time billing event handling and payment provider data synchronization." className="shadow-none" />
<Card title="Custom plans" description="Create and manage custom pricing plans offering flexible billing options." className="shadow-none" />
<Card title="Billing components" description="Ready-made components for pricing, checkout and billing management." className="shadow-none" />
<Card title="Multiple providers" description="Seamless integration of Stripe, LemonSqueezy and Polar payment systems." className="shadow-none" />
</Cards>
### Database
<Cards>
<Card title="Advanced querying" description="Type-safe SQL queries, relational joins, filters, ordering, pagination and more." className="shadow-none" />
<Card title="Schema migrations" description="Automated schema migrations with version control, rollback, and auto-generation." className="shadow-none" />
<Card title="Connection pooling" description="Standalone or serverless database connections with optimal pooling strategies." className="shadow-none" />
<Card title="Data validation" description="End-to-end data validation using shared types and schema definitions." className="shadow-none" />
</Cards>
### API
<Cards>
<Card title="Serverless architecture" description="Modern serverless infrastructure offering auto-scaling and high availability." className="shadow-none" />
<Card title="Single source of truth" description="Unified data management across all apps through shared types and validation." className="shadow-none" />
<Card title="Protected routes" description="Secure API endpoints implementing role-based access control and rate limiting." className="shadow-none" />
<Card title="Feature-based access" description="Access control based on features and subscription plans." className="shadow-none" />
<Card title="Typesafe client" description="Fully typesafe frontend client featuring automatic type generation." className="shadow-none" />
</Cards>
### Admin
<Cards>
<Card title="Super admin UI" description="Centralized admin workspace with overview metrics and quick actions." className="shadow-none" />
<Card title="User management" description="Search, filter and manage users, status, auth methods and MFA." className="shadow-none" />
<Card title="Roles and permissions" description="Granular access control for admins, moderators and support staff." className="shadow-none" />
<Card title="Impersonation" description="Securely impersonate users to reproduce issues and provide support." className="shadow-none" />
</Cards>
### AI
<Cards>
<Card title="Multiple providers" className="shadow-none">
Seamless integration of OpenAI, Anthropic, Groq, Mistral, and Gemini. For more advanced AI features, check out [TurboStarter AI](/ai/docs).
</Card>
<Card title="Ready-to-use components" description="Pre-built chatbot and assistant components supporting real-time streaming." className="shadow-none" />
<Card title="Streaming responses" description="Real-time AI response delivery including progress indicators." className="shadow-none" />
<Card title="Custom rules" description="Includes custom rules and prompts for AI editors and models to make you ship faster." className="shadow-none" />
</Cards>
### Internationalization
<Cards>
<Card title="Locale routing" description="Smart routing based on user locale and automatic language detection." className="shadow-none" />
<Card title="Multiple languages" description="Comprehensive multi-language support and translation management." className="shadow-none" />
<Card title="Language switching" description="One-click language changes and persistent preferences." className="shadow-none" />
<Card title="Mail templates" description="Multi-language email templates including fallback options." className="shadow-none" />
</Cards>
### Emails
<Cards>
<Card title="Transactional emails" description="Automated email delivery including tracking and analytics capabilities." className="shadow-none" />
<Card title="Marketing emails" description="Create and send marketing campaigns using beautiful templates." className="shadow-none" />
<Card title="Email templates" description="Responsive email templates supporting dark mode customization." className="shadow-none" />
<Card title="Multiple providers" description="Easy integration of SendGrid, Resend, and Nodemailer services." className="shadow-none" />
</Cards>
### Landing page
<Cards>
<Card title="Hero section" description="Dynamic hero with subtle animations, platform links, and primary/secondary CTAs." className="shadow-none" />
<Card title="Feature highlights" description="Grid and list layouts to present key features and differentiators." className="shadow-none" />
<Card title="Pricing plans" description="Billing-integrated pricing table with intervals, discounts and footer notes." className="shadow-none" />
<Card title="Testimonials" description="Social proof section with avatars, star ratings and counts." className="shadow-none" />
<Card title="FAQ" description="Structured FAQ with SEO schema for rich results." className="shadow-none" />
<Card title="Re-usable CTAs" description="Buttons, badges and links to docs, pricing and community." className="shadow-none" />
</Cards>
### Marketing
<Cards>
<Card title="SEO" description="Complete SEO toolkit including automatic sitemap generation." className="shadow-none" />
<Card title="Meta tags" description="Flexible meta tag system supporting social media previews." className="shadow-none" />
<Card title="Tips and tricks" description="Comprehensive tips and tricks for optimizing marketing of your app." className="shadow-none" />
<Card title="Mobile onboarding" description="Onboarding flow for mobile apps including custom steps, authentication, and more." className="shadow-none" />
<Card title="Blog" description="Full-featured blog system including categories and RSS feed." className="shadow-none" />
<Card title="Legal pages" description="Pre-built legal templates including version control." className="shadow-none" />
<Card title="Contact form" description="Smart contact form featuring spam protection and auto-responses." className="shadow-none" />
</Cards>
### Storage
<Cards>
<Card title="File uploads" description="Complete file upload system including progress tracking and validation." className="shadow-none" />
<Card title="S3 storage" description="S3-compatible storage offering automatic file optimization." className="shadow-none" />
</Cards>
### CMS
<Cards>
<Card title="Blog pages" description="Complete blog management system including categories and tags." className="shadow-none" />
<Card title="MDX content collections" description="Organized content structure using MDX-based collections and custom frontmatter." className="shadow-none" />
</Cards>
### Theming
<Cards>
<Card title="Built-in themes" description="10+ pre-built themes offering customizable color schemes." className="shadow-none" />
<Card title="Dark mode" description="Built-in dark mode supporting system preference detection." className="shadow-none" />
<Card title="Components CLI" description="Component generation tools following best practices and TypeScript standards." className="shadow-none" />
<Card title="Design system" description="Complete atomic design system including accessibility features." className="shadow-none" />
</Cards>
### Analytics
<Cards>
<Card title="Event tracking" description="Custom event tracking plus automatic session management." className="shadow-none" />
<Card title="Page views" description="Automatic page view capture including bounce rate metrics." className="shadow-none" />
<Card title="User identification" description="Cross-device user tracking and session management." className="shadow-none" />
<Card title="Multiple providers" description="Seamless integration with multiple platforms (e.g. Google Analytics, PostHog, Plausible, Umami, Open Panel, Vemetric)." className="shadow-none" />
</Cards>
### Monitoring
<Cards>
<Card title="Auto-capture exceptions" description="Automatically capture exceptions and errors in your application." className="shadow-none" />
<Card title="Track performance metrics" description="Track performance metrics such as page views, user sessions, and more." className="shadow-none" />
<Card title="Source maps" description="Automatically generate source maps for your application to improve error reporting." className="shadow-none" />
<Card title="Multiple providers" description="Seamless integration with multiple platforms (e.g. Sentry, PostHog)." className="shadow-none" />
</Cards>
### Deployment
<Cards>
<Card title="One-click deploy" description="Simple deployment to your preferred cloud provider." className="shadow-none" />
<Card title="Submission guide" description="Comprehensive guide for app store submissions and requirements." className="shadow-none" />
<Card title="CI/CD workflows" description="Pre-configured deployment pipelines including automated testing." className="shadow-none" />
<Card title="Over-the-air updates" description="Instantly push code or config updates to users without resubmitting to the app store." className="shadow-none" />
</Cards>
### Testing
<Cards>
<Card title="Unit tests" description="Write and run fast unit tests for individual functions and components with instant feedback." className="shadow-none" />
<Card title="Code coverage" description="See precise coverage metrics that show what code is and isn't tested, giving you valuable insight to improve your test suite." className="shadow-none" />
<Card title="Integration tests" description="Test the interaction between different modules or services to ensure everything works together as intended." className="shadow-none" />
<Card title="E2E tests" description="Simulate real user scenarios across the entire stack with automated end-to-end test tools and examples." className="shadow-none" />
</Cards>
## Use like LEGO blocks
The biggest advantage of TurboStarter is its modularity. You can use the entire stack or just the parts you need. It's like LEGO blocks - you can build anything you want with it.
If you don't need a specific feature, feel free to remove it without affecting the rest of the stack.
This approach allows for:
* **Easy feature integration** - plug new features into the kit with minimal changes.
* **Simplified maintenance** - keep the codebase clean and maintainable.
* **Core feature separation** - distinguish between core features and custom features.
* **Additional modules** - easily add modules like billing, CMS, monitoring, logger, mailer, and more.
## Scope of this documentation
While building a SaaS application involves many moving parts, this documentation focuses specifically on TurboStarter. For in-depth information on the underlying technologies, please refer to their respective official documentation.
This documentation will guide you through configuring, running, and deploying the kit, and will provide helpful links to the official documentation of technologies where necessary.
## `llms.txt`
You can access the entire TurboStarter documentation in Markdown format at [/llms.txt](/llms.txt). This can be used to ask any LLM (assuming it has a big enough context window) questions about the TurboStarter based on the most up-to-date documentation.
### Example usage
For instance, to prompt an LLM with questions about the TurboStarter:
1. Copy the documentation contents from [/llms.txt](/llms.txt)
2. Use the following prompt format:
```
Documentation:
{paste documentation here}
---
Based on the above documentation, answer the following:
{your question}
```
## Enjoy!
This documentation is designed to be easy to follow and understand. If you have any questions or need help, feel free to reach out to us at [hello@turbostarter.dev](mailto:hello@turbostarter.dev).
Explore new features, build amazing apps, and have fun! 🚀

View File

@@ -0,0 +1,65 @@
---
title: Cloning repository
description: Get the code to your local machine and start developing your extension.
url: /docs/extension/installation/clone
---
# Cloning repository
<Callout type="info" title="Prerequisite: Git installed">
Ensure you have Git installed on your local machine before proceeding. You can download Git from [here](https://git-scm.com).
</Callout>
## Git clone
Clone the repository using the following command:
```bash
git clone git@github.com:turbostarter/core
```
By default, we're using [SSH](https://docs.github.com/en/authentication/connecting-to-github-with-ssh) for all Git commands. If you don't have it configured, please refer to the [official documentation](https://docs.github.com/en/authentication/connecting-to-github-with-ssh) to set it up.
Alternatively, you can use HTTPS to clone the repository:
```bash
git clone https://github.com/turbostarter/core
```
Another alternative could be to use the [Github CLI](https://cli.github.com/) or [Github Desktop](https://desktop.github.com/) for Git operations.
<Card title="Git clone" description="git-scm.com" href="https://git-scm.com/docs/git-clone" />
## Git remote
After cloning the repository, remove the original origin remote:
```bash
git remote rm origin
```
Add the upstream remote pointing to the original repository to pull updates:
```bash
git remote add upstream git@github.com:turbostarter/core
```
Once you have your own repository set up, add your repository as the origin:
```bash
git remote add origin <your-repository-url>
```
<Card title="Git remote" description="git-scm.com" href="https://git-scm.com/docs/git-remote" />
## Staying up to date
To pull updates from the upstream repository, run the following command daily (preferably with your morning coffee ☕):
```bash
git pull upstream main
```
This ensures your repository stays up to date with the latest changes.
Check [Updating codebase](/docs/web/installation/update) for more details on updating your codebase.

View File

@@ -0,0 +1,353 @@
---
title: Common commands
description: Learn about common commands you need to know to work with the extension project.
url: /docs/extension/installation/commands
---
# Common commands
<Callout>
For sure, you don't need these commands to kickstart your project, but it's useful to know they exist for when you need them.
</Callout>
<Callout title="Want shorter commands?">
You can set up aliases for these commands in your shell configuration file. For example, you can set up an alias for `pnpm` to `p`:
```bash title="~/.bashrc"
alias p='pnpm'
```
Or, if you're using [Zsh](https://ohmyz.sh/), you can add the alias to `~/.zshrc`:
```bash title="~/.zshrc"
alias p='pnpm'
```
Then run `source ~/.bashrc` or `source ~/.zshrc` to apply the changes.
You can now use `p` instead of `pnpm` in your terminal. For example, `p i` instead of `pnpm install`.
</Callout>
<Callout title="Injecting environment variables">
To inject environment variables into the command you run, prefix it with `with-env`:
```bash
pnpm with-env <command>
```
For example, `pnpm with-env pnpm build` will run `pnpm build` with the environment variables injected.
Some commands, like `pnpm dev`, automatically inject the environment variables for you.
</Callout>
## Installing dependencies
To install the dependencies, run:
```bash
pnpm install
```
## Starting development server
Start development server by running:
```bash
pnpm dev
```
## Building project
To build the project (including all apps and packages), run:
```bash
pnpm build
```
## Building specific app/package
To build a specific app/package, run:
```bash
pnpm turbo build --filter=<package-name>
```
## Cleaning project
To clean the project, run:
```bash
pnpm clean
```
Then, reinstall the dependencies:
```bash
pnpm install
```
## Formatting code
To check for formatting errors using Prettier, run:
```bash
pnpm format
```
To fix formatting errors using Prettier, run:
```bash
pnpm format:fix
```
## Linting code
To check for linting errors using ESLint, run:
```bash
pnpm lint
```
To fix linting errors using ESLint, run:
```bash
pnpm lint:fix
```
## Typechecking
To typecheck the code using TypeScript for any type errors, run:
```bash
pnpm typecheck
```
## Adding UI components
<Tabs items={["Web", "Mobile"]}>
<Tab value="Web">
To add a new web component, run:
```bash
pnpm --filter @turbostarter/ui-web ui:add
```
This command will add and export a new component to `@turbostarter/ui-web` package.
</Tab>
<Tab value="Mobile">
To add a new mobile component, run:
```bash
pnpm --filter @turbostarter/ui-mobile ui:add
```
This command will add and export a new component to `@turbostarter/ui-mobile` package.
</Tab>
</Tabs>
## Services commands
<Callout title="Prerequisite: Docker installed">
To run the services containers locally, you need to have [Docker](https://www.docker.com/) installed on your machine.
You can always use the cloud-hosted solution (e.g. [Neon](https://neon.tech/), [Turso](https://turso.tech/) for database) for your projects.
</Callout>
We have a few commands to help you manage the services containers (for local development).
### Starting containers
To start the services containers, run:
```bash
pnpm services:start
```
It will run all the services containers. You can check their configs in `docker-compose.yml`.
### Setting up services
To setup all the services, run:
```bash
pnpm services:setup
```
It will start all the services containers and run necessary setup steps.
### Stopping containers
To stop the services containers, run:
```bash
pnpm services:stop
```
### Displaying status
To check the status and logs of the services containers, run:
```bash
pnpm services:status
```
### Displaying logs
To display the logs of the services containers, run:
```bash
pnpm services:logs
```
### Database commands
We have a few commands to help you manage the database leveraging [Drizzle CLI](https://orm.drizzle.team/kit-docs/commands).
#### Generating migrations
To generate a new migration, run:
```bash
pnpm with-env turbo db:generate
```
It will create a new migration `.sql` file in the `packages/db/migrations` folder.
#### Running migrations
To run the migrations against the db, run:
```bash
pnpm with-env pnpm --filter @turbostarter/db db:migrate
```
It will apply all the pending migrations.
#### Pushing changes directly
<Callout type="warn" title="Don't mess up with your schema!">
Make sure you know what you're doing before pushing changes directly to the db.
</Callout>
To push changes directly to the db, run:
```bash
pnpm with-env pnpm --filter @turbostarter/db db:push
```
It lets you push your schema changes directly to the database and omit managing SQL migration files.
#### Checking database status
To check the status of the database, run:
```bash
pnpm with-env pnpm --filter @turbostarter/db db:status
```
It will display the status of the applied migrations and the pending ones.
```bash
Applied migrations:
- 0000_cooing_vargas
- 0001_curious_wallflower
- 0002_good_vertigo
- 0003_peaceful_devos
- 0004_fat_mad_thinker
- 0005_yummy_bucky
- 0006_glorious_vargas
Pending migrations:
- 0007_nebulous_havok
```
#### Resetting database
To reset the database, run:
```bash
pnpm with-env pnpm --filter @turbostarter/db db:reset
```
It will reset the database to the initial state.
#### Seeding database
To seed the database with some example data (for development purposes), run:
```bash
pnpm with-env turbo db:seed
```
It will populate your database with some example data.
#### Checking database
To check the database schema consistency, run:
```bash
pnpm with-env pnpm --filter @turbostarter/db db:check
```
#### Studying database
To study the database schema in the browser, run:
```bash
pnpm with-env pnpm --filter @turbostarter/db db:studio
```
This will start the Studio on [https://local.drizzle.studio](https://local.drizzle.studio).
## Tests commands
### Running tests
To run the tests, run:
```bash
pnpm test
```
This will run all the tests in the project using Turbo tasks. As it leverages Turbo caching, it's [recommended](/docs/web/tests/unit#configuration) to run it in your CI/CD pipeline.
### Running tests projects
To run tests for all Vitest [Test Projects](https://vitest.dev/guide/projects), run:
```bash
pnpm test:projects
```
This will run all the tests in the project using Vitest.
### Watching tests
To watch the tests, run:
```bash
pnpm test:projects:watch
```
This will watch the tests for all [Test Projects](https://vitest.dev/guide/projects) and run them automatically when you make changes.
### Generating code coverage
To generate code coverage report, run:
```bash
pnpm turbo test:coverage
```
This will generate a code coverage report in the `coverage` directory under `tooling/vitest` package.
### Viewing code coverage
To preview the code coverage report in the browser, run:
```bash
pnpm turbo test:coverage:view
```
This will launch the report's `.html` file in your default browser.

View File

@@ -0,0 +1,86 @@
---
title: Conventions
description: Some standard conventions used across TurboStarter codebase.
url: /docs/extension/installation/conventions
---
# Conventions
You're not required to follow these conventions: they're simply a standard set of practices used in the core kit. If you like them - we encourage you to keep these during your usage of the kit - so to have consistent code style that you and your teammates understand.
## Turborepo Packages
In this project, we use [Turborepo packages](https://turbo.build/repo/docs/core-concepts/internal-packages) to define reusable code that can be shared across multiple applications.
* **Apps** are used to define the main application, including routing, layout, and global styles.
* **Packages** shares reusable code add functionalities across multiple applications. They're configurable from the main application.
<Callout title="Should I create a new package?">
**Recommendation:** Do not create a package for your app code unless you plan to reuse it across multiple applications or are experienced in writing library code.
If your application is not intended for reuse, keep all code in the app folder. This approach saves time and reduces complexity, both of which are beneficial for fast shipping.
**Experienced developers:** If you have the experience, feel free to create packages as needed.
</Callout>
## Imports and Paths
When importing modules from packages or apps, use the following conventions:
* **From a package:** Use `@turbostarter/package-name` (e.g., `@turbostarter/ui`, `@turbostarter/api`, etc.).
* **From an app:** Use `~/` (e.g., `~/components`, `~/config`, etc.).
## Enforcing conventions
* [Prettier](https://prettier.io/) is used to enforce code formatting.
* [ESLint](https://eslint.org/) is used to enforce code quality and best practices.
* [TypeScript](https://www.typescriptlang.org/) is used to enforce type safety.
<Cards className="grid-cols-2 sm:grid-cols-3">
<Card title="Prettier" href="https://prettier.io/" description="prettier.io" />
<Card title="ESLint" href="https://eslint.org/" description="eslint.org" />
<Card title="TypeScript" href="https://www.typescriptlang.org/" description="typescriptlang.org" />
</Cards>
## Code health
TurboStarter provides a set of tools to ensure code health and quality in your project.
### Github Actions
By default, TurboStarter sets up Github Actions to run tests on every push to the repository. You can find the Github Actions configuration in the `.github/workflows` directory.
The workflow has multiple stages:
* `format` - runs Prettier to format the code.
* `lint` - runs ESLint to check for linting errors.
* `typecheck` - runs TypeScript to check for type errors.
### Git hooks
Together with TurboStarter we have set up a `commit-msg` hook which will check if your commit message follows the [conventional commit](https://www.conventionalcommits.org/en/v1.0.0/) message format. This is important for generating changelogs and keeping a clean commit history.
Although we didn't ship any pre-commit hooks (we believe in shipping fast with moving checking code responsibility to CI), you can easily add them by using [Husky](https://typicode.github.io/husky/#/).
#### Setting up the Pre-Commit Hook
To do so, create a `pre-commit` file in the `./..husky` directory with the following content:
```bash
#!/bin/sh
pnpm typecheck
pnpm lint
```
Turborepo will execute the commands for all the affected packages - while skipping the ones that are not affected.
#### Make the Pre-Commit Hook Executable
```bash
chmod +x ./.husky/pre-commit
```
To test the pre-commit hook, try to commit a file with linting errors or type errors. The commit should fail, and you should see the error messages in the console.

View File

@@ -0,0 +1,73 @@
---
title: Managing dependencies
description: Learn how to manage dependencies in your project.
url: /docs/extension/installation/dependencies
---
# Managing dependencies
As the package manager we chose [pnpm](https://pnpm.io/).
<Callout title="Why pnpm?">
It is a fast, disk space efficient package manager that uses hard links and symlinks to save one version of a module only ever once on a disk. It also has a great [monorepo support](https://pnpm.io/workspaces). Of course, you can change it to use [Bun](https://bunpkg.com), [yarn](https://yarnpkg.com) or [npm](https://www.npmjs.com) with minimal effort.
</Callout>
## Install dependency
To install a package you need to decide whether you want to install it to the root of the monorepo or to a specific workspace. Installing it to the root makes it available to all packages, while installing it to a specific workspace makes it available only to that workspace.
To install a package globally, run:
```bash
pnpm add -w <package-name>
```
To install a package to a specific workspace, run:
```bash
pnpm add --filter <workspace-name> <package-name>
```
For example:
```bash
pnpm add --filter @turbostarter/ui motion
```
It will install `motion` to the `@turbostarter/ui` workspace.
## Remove dependency
Removing a package is the same as installing but with the `remove` command.
To remove a package globally, run:
```bash
pnpm remove -w <package-name>
```
To remove a package from a specific workspace, run:
```bash
pnpm remove --filter <workspace-name> <package-name>
```
## Update a package
Updating is a bit easier since there is a nice way to update a package in all workspaces at once:
```bash
pnpm update -r <package-name>
```
<Callout title="Semantic versioning">
When you update a package, pnpm will respect the [semantic versioning](https://docs.npmjs.com/about-semantic-versioning) rules defined in the `package.json` file. If you want to update a package to the latest version, you can use the `--latest` flag.
</Callout>
## Renovate bot
By default, TurboStarter comes with [Renovate](https://www.npmjs.com/package/renovate) enabled. It is a tool that helps you manage your dependencies by automatically creating pull requests to update your dependencies to the latest versions. You can find its configuration in the `.github/renovate.json` file. Learn more about it in the [official docs](https://docs.renovatebot.com/configuration-options/).
When it creates a pull request, it is treated as a normal PR, so all tests and preview deployments will run. **It is recommended to always preview and test the changes in the staging environment before merging the PR to the main branch to avoid breaking the application.**
<Card href="https://docs.renovatebot.com" title="Renovate" description="renovatebot.com" />

View File

@@ -0,0 +1,139 @@
---
title: Development
description: Get started with the code and develop your browser extension.
url: /docs/extension/installation/development
---
# Development
## Prerequisites
To get started with TurboStarter, ensure you have the following installed and set up:
* [Node.js](https://nodejs.org/en) (22.x or higher)
* [Docker](https://www.docker.com) (only if you want to use local services e.g. database)
* [pnpm](https://pnpm.io)
## Project development
<Steps>
<Step>
### Install dependencies
Install the project dependencies by running the following command:
```bash
pnpm i
```
<Callout title="Why pnpm?">
It is a fast, disk space efficient package manager that uses hard links and symlinks to save one version of a module only ever once on a disk. It also has a great [monorepo support](https://pnpm.io/workspaces). Of course, you can change it to use [Bun](https://bunpkg.com), [yarn](https://yarnpkg.com) or [npm](https://www.npmjs.com) with minimal effort.
</Callout>
</Step>
<Step>
### Setup environment variables
Create a `.env.local` files from `.env.example` files and fill in the required environment variables.
You can use the following command to recursively copy the `.env.example` files to the `.env.local` files:
<Tabs items={["Unix (MacOS/Linux)", "Windows"]}>
<Tab value="Unix (MacOS/Linux)">
```bash
find . -name ".env.example" -exec sh -c 'cp "$1" "${1%.example}.local"' _ {} \;
```
</Tab>
<Tab value="Windows">
```bash
Get-ChildItem -Recurse -Filter ".env.example" | ForEach-Object {
Copy-Item $_.FullName ($\_.FullName -replace '\.example$', '.local')
}
```
</Tab>
</Tabs>
Check [Environment variables](/docs/extension/configuration/environment-variables) for more details on setting up environment variables.
</Step>
<Step>
### Setup services
If you want to use local services like database etc. (**recommended for development purposes**), ensure Docker is running, then setup them with:
```bash
pnpm services:setup
```
This command initiates the containers and runs necessary setup steps, ensuring your services are up to date and ready to use.
</Step>
<Step>
### Start development server
To start the application development server, run:
```bash
pnpm dev
```
Your development server should now be running 🎉
WXT will create a dev bundle for your extension and start a live-reloading development server, which will automatically update your extension bundle and reload your browser on source code changes.
It also makes the icon grayscale to distinguish between development and production extension bundles.
</Step>
<Step>
### Load the extension
<Tabs items={["Chrome", "Firefox"]}>
<Tab value="Chrome">
Head over to `chrome://extensions` and enable **Developer Mode**.
![Developer mode](/images/docs/extension/chrome/developer-mode.png)
Click on "Load Unpacked" and navigate to your extension's `apps/extension/build/chrome-mv3` directory.
To see your popup, click on the puzzle piece icon on the Chrome toolbar, and click on your extension.
![Pin to toolbar](/images/docs/extension/chrome/pin.png)
<Callout title="Pro tip">
Pin your extension to the Chrome toolbar for easy access by clicking the pin button.
</Callout>
</Tab>
<Tab value="Firefox">
Head over to `about:debugging` and click on "This Firefox".
Click on "Load Temporary Add-on" and navigate to your extension's `apps/extension/build/firefox-mv2` directory. Pick any file to load the extension.
![Load temporary add-on](/images/docs/extension/firefox/load.png)
The extension now installs, and remains installed until you restart Firefox.
To see your popup, click on your extension icon on the Firefox toolbar.
![Popup](/images/docs/extension/firefox/popup.png)
<Callout>
Loaded extension starts as pinned on the Firefox toolbar. Don't remove it to easily access it later.
</Callout>
</Tab>
</Tabs>
<Callout title="Automatic browser startup">
You can also configure your development server to automatically start the browser when you start the server. To do it, create a `web-ext.config.ts` file in a root of your extension and configure it with your browser [binaries](https://wxt.dev/guide/essentials/config/browser-startup.html#set-browser-binaries) and [argumens](https://wxt.dev/guide/essentials/config/browser-startup.html#persist-data).
Learn more in the [official documentation](https://wxt.dev/guide/essentials/config/browser-startup.html).
</Callout>
</Step>
<Step>
### Publish to stores
When you're ready to publish the project to the stores, follow the [guidelines](/docs/extension/marketing) and [checklist](/docs/extension/publishing/checklist) to ensure everything is set up correctly.
</Step>
</Steps>

View File

@@ -0,0 +1,69 @@
---
title: Editor setup
description: Learn how to set up your editor for the fastest development experience.
url: /docs/extension/installation/editor-setup
---
# Editor setup
Of course you can use every IDE you like, but you will have the best possible developer experience with this starter kit when using **VSCode-based** editor with the suggested settings and extensions.
## Settings
We have included most recommended settings in the `.vscode/settings.json` file to make your development experience as smooth as possible. It include mostly configs for tools like Prettier, ESLint and Tailwind which are used to enforce some conventions across the codebase. You can adjust them to your needs.
## LLM rules
We exposed a special endpoint that will scan all the docs and return the content as a text file which you can use to train your LLM or put in a prompt. You can find it at [/llms.txt](/llms.txt).
The repository also includes a custom rules for most popular AI editors and agents to ensure that AI completions are working as expected and following our conventions.
### AGENTS.md
We've integrated specific rules that help maintain code quality and ensure AI-assisted completions align with our project standards.
You can find them in the `AGENTS.md` file at the root of the project. This format is a standardized way to instruct AI agents to follow project conventions when generating code and it's used by over **20,000** open-source projects - including [Cursor](https://cursor.sh/), [Aider](https://aider.chat/), [Codex from OpenAI](https://openai.com/blog/openai-codex/), [Jules from Google](https://jules.ai/), [Windsurf](https://windsurf.dev/), and many others.
```md title="AGENTS.md"
### Code Style and Structure
- Write concise, technical TypeScript code with accurate examples
- Use functional and declarative programming patterns; avoid classes
- Prefer iteration and modularization over code duplication
### Naming Conventions
....
```
To learn more about `AGENTS.md` rules check out the [official documentation](https://agents.md/).
## Extensions
Once you cloned the project and opened it in VSCode you should be promted to install suggested extensions which are defined in the `.vscode/extensions.json` automatically. In case you rather want to install them manually you can do so at any time later.
These are the extensions we recommend:
### ESLint
Global extension for static code analysis. It will help you to find and fix problems in your JavaScript code.
<Card title="Download ESLint" href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint" description="marketplace.visualstudio.com" />
### Prettier
Global extension for code formatting. It will help you to keep your code clean and consistent.
<Card title="Download Prettier" href="https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode" description="marketplace.visualstudio.com" />
### Pretty TypeScript Errors
Improves TypeScript error messages shown in the editor.
<Card title="Download Pretty TypeScript Errors" href="https://marketplace.visualstudio.com/items?itemName=yoavbls.pretty-ts-errors" description="marketplace.visualstudio.com" />
### Tailwind CSS IntelliSense
Adds IntelliSense for Tailwind CSS classes to enable autocompletion and linting.
<Card title="Download Tailwind CSS IntelliSense" href="https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss" description="marketplace.visualstudio.com" />

View File

@@ -0,0 +1,128 @@
---
title: Project structure
description: Learn about the project structure and how to navigate it.
url: /docs/extension/installation/structure
---
# Project structure
The main directories in the project are:
* `apps` - the location of the main apps
* `packages` - the location of the shared code and the API
### `apps` Directory
This is where the apps live. It includes web app (Next.js), mobile app (React Native - Expo), and the browser extension (WXT - Vite + React). Each app has its own directory.
### `packages` Directory
This is where the shared code and the API for packages live. It includes the following:
* shared libraries (database, mailers, cms, billing, etc.)
* shared features (auth, mails, billing, ai etc.)
* UI components (buttons, forms, modals, etc.)
All apps can use and reuse the API exported from the packages directory. This makes it easy to have one, or many apps in the same codebase, sharing the same code.
## Repository structure
By default the monorepo contains the following apps and packages:
<Files>
<Folder name="apps" defaultOpen>
<Folder name="web - Web app (Next.js)" />
<Folder name="mobile - Mobile app (React Native - Expo)" />
<Folder name="extension - Browser extension (WXT - Vite + React)" />
</Folder>
<Folder name="packages" defaultOpen>
<Folder name="analytics - Analytics setup" />
<Folder name="api - API server (including all features logic)" />
<Folder name="auth - Authentication setup" />
<Folder name="billing - Billing config and providers" />
<Folder name="cms - CMS setup and providers" />
<Folder name="db - Database setup" />
<Folder name="email - Mail templates and providers" />
<Folder name="i18n - Internationalization setup" />
<Folder name="shared - Shared utilities and helpers" />
<Folder name="storage - Storage setup" />
<Folder name="ui - Atomic UI components">
<Folder name="shared" />
<Folder name="web" />
<Folder name="mobile" />
</Folder>
</Folder>
<Folder name="tooling" defaultOpen>
<Folder name="eslint - ESLint config" />
<Folder name="github - Github actions" />
<Folder name="prettier - Prettier config" />
<Folder name="typescript - TypeScript config" />
</Folder>
</Files>
## Browser extension application structure
The browser extension application is located in the `apps/extension` folder. It contains the following folders:
<Files>
<Folder name="src" defaultOpen>
<Folder name="app" defaultOpen>
<Folder name="background - Background service worker" />
<Folder name="content - Content scripts" />
<Folder name="devtools - Devtools page with custom panels" />
<Folder name="newtab - New tab page" />
<Folder name="options - Options page" />
<Folder name="popup - Popup window" />
<Folder name="sidepanel - Side panel" />
<Folder name="tabs - Custom pages shipped with the extension" />
</Folder>
<Folder name="assets - Optimized static assets" />
<Folder name="config - App config" />
<Folder name="lib - Communication with packages" />
<Folder name="modules - Application modules" />
</Folder>
<File name=".env.local" />
<File name="env.config.ts" />
<File name="eslint.config.js" />
<File name="package.json" />
<File name="tsconfig.json" />
<File name="turbo.json" />
<File name="wxt.config.ts" />
</Files>

View File

@@ -0,0 +1,97 @@
---
title: Updating codebase
description: Learn how to update your codebase to the latest version.
url: /docs/extension/installation/update
---
# Updating codebase
If you've been following along with our previous guides, you should already have a Git repository set up for your project, with an `upstream` remote pointing to the original repository.
Updating your project involves fetching the latest changes from the `upstream` remote and merging them into your project. Let's dive into the steps!
<Steps>
<Step>
## Stash changes
<Callout title="Don't have changes?">
If you don't have any changes to stash, you can skip this step and proceed with the update process.
Alternatively, you can [commit](https://git-scm.com/docs/git-commit) your changes.
</Callout>
If you have any uncommitted changes, stash them before proceeding. It will allow you to avoid any conflicts that may arise during the update process.
```bash
git stash
```
This command will save your changes in a temporary location, allowing you to retrieve them later. Once you're done updating, you can apply the stash to your working directory.
```bash
git stash apply
```
</Step>
<Step>
## Pull changes
Pull the latest changes from the `upstream` remote.
```bash
git pull upstream main
```
When prompted the first time, please opt for merging instead of rebasing.
Don't forget to run `pnpm i` in case there are any updates in the dependencies.
</Step>
<Step>
## Resolve conflicts
If there are any conflicts during the merge, Git will notify you. You can resolve them by opening the conflicting files in your code editor and making the necessary changes.
<Callout title="Conflicts in pnpm-lock.yaml?">
If you find conflicts in the `pnpm-lock.yaml file`, accept either of the two changes (avoid manual edits), then run:
```bash
pnpm i
```
Your lock file will now reflect both your changes and the updates from the upstream repository.
</Callout>
</Step>
<Step>
## Run a health check
After resolving the conflicts, it's time to test your project to ensure everything is working as expected. Run your project locally and navigate through the various features to verify that everything is functioning correctly.
For a quick health check, you can run:
```bash
pnpm lint
pnpm typecheck
```
If everything looks good, you're all set! Your project is now up to date with the latest changes from the `upstream` repository.
</Step>
<Step>
## Commit and push
Once everything is working fine, don't forget to commit your changes using:
```bash
git commit -m "<your-commit-message>"
```
and push them to your remote repository with:
```bash
git push origin <your-branch-name>
```
</Step>
</Steps>

View File

@@ -0,0 +1,175 @@
---
title: Internationalization
description: Learn how to internationalize your extension.
url: /docs/extension/internationalization
---
# Internationalization
Turbostarter's extension uses [i18next](https://www.i18next.com/) and web cookies to store the language preference of the user. This allows the extension to be fully internationalized.
<Callout title="Why this combination?">
We use i18next because it's a robust and widely-adopted internationalization framework that works seamlessly with React.
The combination with web cookies allows us to persistently store language preferences across all extension contexts and share it with the web app while maintaining excellent performance and browser compatibility.
</Callout>
![i18next logo](/images/docs/i18next.jpg)
## Configuration
The global configuration is defined in the `@turbostarter/i18n` package and shared across all applications. You can read more about it in the [web configuration](/docs/web/internationalization/configuration) documentation.
By default, the locale is automatically detected based on the user's device settings. You can override it and set the default locale of your mobile app in the [app configuration](/docs/extension/configuration/app) file.
Also, the locale configuration is **shared between the web app and the extension** (same as [session](/docs/extension/auth/session)), which means that changing the locale in one place will automatically update it in the other. It's a common pattern for modern apps, simplifying the user experience and reducing the maintenance effort.
### Cookies
When a user first opens the [web app](/docs/web), the locale is detected and a cookie is set. This cookie is used to remember the user's language preference.
You can find its value in the *Cookies* tab of the developer tools of your browser:
![Locale cookie](/images/docs/extension/locale-cookie.png)
To enable your extension to read the cookie and that way share the locale settings with the web app, you need to set the cookies permission in the `wxt.config.ts` under `manifest.permissions` field:
```ts
export default defineConfig({
manifest: {
permissions: ["cookies"],
},
});
```
And to be able to read the cookie from your app url, you need to set host\_permissions, which will include your app url:
```ts
export default defineConfig({
manifest: {
host_permissions: ["http://localhost/*", "https://your-app-url.com/*"],
},
});
```
Then you would be able to share the cookie between your apps and also read its value using `browser.cookies` API.
<Callout title="Avoid &#x22;<all_urls>&#x22;" type="warn">
Avoid using `<all_urls>` in `host_permissions`. It affects all urls and may cause security issues, as well as a [rejection](https://developer.chrome.com/docs/webstore/review-process#review-time-factors) from the destination store.
</Callout>
<Cards>
<Card title="Declare permissions" href="https://developer.chrome.com/docs/extensions/develop/concepts/declare-permissions" description="developer.chrome.com" />
<Card title="chrome.cookies" href="https://developer.chrome.com/docs/extensions/reference/api/cookies" description="developer.chrome.com" />
</Cards>
## Translating extension
To translate individual components and screens, you can use the well-known `useTranslation` hook.
```tsx
import { useTranslation } from "@turbostarter/i18n";
export const Popup = () => {
const { t } = useTranslation();
return <div>{t("hello")}</div>;
};
```
That's the recommended way to translate stuff inside your extension.
### Store presence
As we saw in the [manifest](/docs/extension/configuration/manifest#locales) section, you can also localize your extension's store presence (like title, description, and other metadata). This allows you to customize how your extension appears in different web stores based on the user's language.
Each store has specific requirements for localization:
* [Chrome Web Store](https://developer.chrome.com/docs/webstore/cws-dashboard-listing/) requires a `_locales` directory with JSON files for each language
* [Firefox Add-ons](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Internationalization) uses a similar structure but with some differences in the manifest
* [Edge Add-ons](https://learn.microsoft.com/en-us/microsoft-edge/extensions/publish/publish-extension#supporting-multiple-languages) uses the same structure as Chrome Web Store
Although most of the config is abstracted behind common structure, please follow the store-specific guides below for detailed instructions on setting up localization for your extension's store listing.
<Cards>
<Card title="I18n - WXT" href="https://wxt.dev/guide/essentials/i18n.html" description="wxt.dev" />
<Card title="Chrome Web Store" href="https://developer.chrome.com/docs/webstore/cws-dashboard-listing" description="developer.chrome.com" />
<Card title="Firefox Add-ons" href="https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Internationalization" description="developer.mozilla.org" />
<Card title="Edge Add-ons" href="https://learn.microsoft.com/en-us/microsoft-edge/extensions/publish/publish-extension#supporting-multiple-languages" description="developer.microsoft.com" />
</Cards>
## Language switcher
TurboStarter ships with a language customizer component that allows users to switch between languages in your extension. You can import and use the `LocaleCustomizer` component in your popup, options page, or any other extension view:
```tsx
import { LocaleCustomizer } from "@turbostarter/ui-web/i18n";
export const Popup = () => {
return <LocaleCustomizer />;
};
```
<Callout title="This will change the locale of the web app as well" type="warn">
As the web app and extension share the same i18n configuration (cookie), changing the language in one will affect the other. **This is intentional** and ensures a consistent experience across both platforms, since your extension likely serves as a companion to the web app and should maintain the same language preferences.
</Callout>
## Best practices
Here are key best practices for managing translations in your browser extension:
* Use descriptive, hierarchical translation keys
```ts
// ✅ Good
"popup.settings.language";
"content.toolbar.save";
// ❌ Bad
"saveButton";
"text1";
```
* Organize translations by extension views and features
```
_locales/
├── en/
│ ├── messages.json
│ ├── popup.json
│ └── options.json
└── es/
├── messages.json
├── popup.json
└── options.json
```
* Handle fallback languages gracefully
* Keep manifest descriptions localized for store listings
* Consider context in translations:
```ts
// Context-aware messages
t("button.save", { context: "document" }); // "Save document"
t("button.save", { context: "settings" }); // "Apply changes"
```
* Use placeholders for dynamic content:
```ts
// With variables
t("status.saved", { time: "2 minutes ago" }); // "Last saved 2 minutes ago"
// With plurals
t("items", { count: 5 }); // "5 items"
```
* Keep translations in sync between extension views
* Cache translations for offline functionality

View File

@@ -0,0 +1,63 @@
---
title: Marketing
description: Learn how to market your mobile application.
url: /docs/extension/marketing
---
# Marketing
As you saw in the [Extras](/docs/extension/extras) section, TurboStarter comes with a lot of tips and tricks to make your product better and help you launch your extension faster with higher traffic.
The same applies to [submission tips](/docs/extension/extras#submission-tips) to help you get your extension approved by the browser stores faster.
We'll talk more about the whole process of deploying and publishing your extension in the [Publishing](/docs/extension/publishing/checklist) section, here we'll go through some guidelines that you need to follow to make your store's visibility higher.
## Before you submit
To help your extension approval go as smoothly as possible, review the common missteps listed below that can slow down the review process or trigger a rejection. This doesn't replace the official guidelines or guarantee approval, but making sure you can check every item on the list is a good start.
Make sure you:
* Test your extension thoroughly for crashes and bugs
* Ensure that all extension information and metadata is complete and accurate
* Update your contact information in case the review team needs to reach you
* Provide clear instructions on how to use your extension, including any special setup required
* If your extension requires an account, provide a demo account or a way to test all features without signing up
* Enable and test all backend services to ensure they're live and accessible during review
* Include detailed explanations of non-obvious features in the extension description
* Ensure your extension complies with the specific browser store's policies (e.g., [Chrome Web Store](https://developer.chrome.com/docs/webstore/program-policies/best-practices), [Firefox Add-ons](https://extensionworkshop.com/documentation/publish/add-on-policies/), [Edge Add-ons](https://learn.microsoft.com/en-us/legal/microsoft-edge/extensions/developer-policies) etc.)
* Remove any references to features not supported in browser extensions (e.g., in-app purchases)
Following these basic steps during development and before submission will help you get your extension approved faster and with fewer issues.
## Guidelines
Each store has slightly different guidelines, but some of them are general and can be applied to all stores:
* **Security**: Your extension must not contain malicious code or behavior that can harm users' devices or data.
* **Performance**: Your extension must be performant and stable, with a smooth user experience.
* **Privacy**: Your extension must respect user privacy and not collect unnecessary data without explicit consent.
* **Compliance**: Your extension must comply with all relevant laws and regulations.
You can read more about official guidelines for each store in the following links:
* [Chrome Web Store](https://developer.chrome.com/docs/webstore/program-policies/best-practices)
* [Firefox Add-ons](https://extensionworkshop.com/documentation/publish/add-on-policies/)
* [Edge Add-ons](https://learn.microsoft.com/en-us/microsoft-edge/extensions/developer-guide/best-practices)
## Common mistakes
There are a few common mistakes that you should avoid to make sure your extension can be accepted in the stores. The most common ones are:
* **Not enough description** - make sure to describe all the features of your extension and how it works in your store listing, that way users won't be confused about what your extension does. Also include detailed information in the single purpose field regarding your extension's primary functionality.
* **Privacy issues** - respect user privacy and require as least permissions as possible, don't ask for permissions that are not necessary for your extension to work
* **Customer support** - provide a way to contact you in case the user has any issues with your extension
* **Stay up-to-date** - keep your extension and its documentation up-to-date to ensure a smooth user experience and to prevent issues during the review process.
<Cards>
<Card href="https://developer.chrome.com/docs/webstore/program-policies/best-practices" title="Best Practices and Guidelines" description="developer.chrome.com" />
<Card href="https://extensionworkshop.com/documentation/publish/add-on-policies/" title="Add-on Policies" description="extensionworkshop.com" />
<Card href="https://learn.microsoft.com/en-us/legal/microsoft-edge/extensions/developer-policies" title="Developer Policies" description="learn.microsoft.com" />
</Cards>

View File

@@ -0,0 +1,143 @@
---
title: Overview
description: Get started with browser extension monitoring in TurboStarter.
url: /docs/extension/monitoring/overview
---
# Overview
TurboStarter includes powerful, provider-agnostic monitoring helpers for the browser extension so you can understand **what failed**, **where it failed** (popup, content script, background), and **who it impacted**. The API is intentionally designed for simplicity and extensibility, so you can swap providers without rewriting your extension code.
## Capturing exceptions
Extensions have multiple runtimes. To get good coverage, capture errors in the places users actually feel them:
* **Popup / options UI**: React pages where runtime errors break interactions.
* **Background (service worker)**: long-lived logic like alarms, message routing, and sync.
* **Content scripts**: page integrations where DOM differences and CSP can trigger failures.
* **Manual reporting**: wrap critical flows (auth, billing, webhooks-to-extension sync, imports) with `try/catch` and report with context.
<Tabs items={["Popup / options", "Background", "Content script"]}>
<Tab value="Popup / options">
```tsx
import { captureException } from "@turbostarter/monitoring-extension";
export function ExampleButton() {
const onPress = async () => {
try {
/* some risky operation */
} catch (error) {
captureException(error);
}
};
return <button onClick={onPress}>Trigger Exception</button>;
}
```
</Tab>
<Tab value="Background">
```ts
import { captureException } from "@turbostarter/monitoring-extension";
browser.runtime.onMessage.addListener((message, _sender, sendResponse) => {
try {
/* handle message */
sendResponse({ ok: true });
} catch (error) {
captureException(error);
sendResponse({ ok: false });
}
});
```
</Tab>
<Tab value="Content script">
```ts
import { captureException } from "@turbostarter/monitoring-extension";
try {
/* interact with the page DOM */
} catch (error) {
captureException(error);
}
```
</Tab>
</Tabs>
<Callout type="warn" title="Don't rely on a single runtime">
An exception in a content script won't automatically show up in your background logs (and vice versa). Add capture points in each runtime you ship, especially if you do message passing between them.
</Callout>
## Identifying users
Monitoring becomes far more useful once reports can be tied to a stable identity. In extensions you often have two “identities”:
* **Anonymous, stable install id**: useful before sign-in (and to correlate issues with a device/install).
* **Signed-in user**: once the user authenticates, identify with their user id so issues map to a real account.
TurboStarter's monitoring layer supports identifying the current user when your auth session resolves. When signed out, pass `null` (or your provider's preferred anonymous identity strategy).
```tsx title="monitoring.tsx"
import { useEffect } from "react";
import { identify } from "@turbostarter/monitoring-extension";
import { authClient } from "~/lib/auth/client";
export const MonitoringProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const session = authClient.useSession();
useEffect(() => {
if (session.isPending) {
return;
}
identify(session.data?.user ?? null);
}, [session]);
return <>{children}</>;
};
```
<Callout title="Privacy defaults" type="error">
Prefer **stable IDs** over PII. Only attach traits that help debugging (plan, role, extension version) and avoid secrets (tokens, passwords) or sensitive fields unless you've explicitly chosen to send them.
</Callout>
## Providers
The starter supports multiple monitoring providers behind the same API, so you can start with one and switch later.
<Cards>
<Card title="Sentry" href="/docs/extension/monitoring/sentry" />
<Card title="PostHog" href="/docs/extension/monitoring/posthog" />
</Cards>
## Best practices
<Cards>
<Card title="Include runtime + version context" className="shadow-none">
Extension issues are often environment-specific. Make sure you can filter by
runtime (popup/background/content script), extension version, and browser.
</Card>
<Card title="Capture actionable failures" className="shadow-none">
Focus on crashes and failures that break core flows; skip “expected” states
like validation errors or user cancellations.
</Card>
<Card title="Dedupe noisy loops" className="shadow-none">
Background alarms, retries, and message loops can generate many identical
errors. Guard your capture calls to keep signal high (and costs low).
</Card>
<Card title="Keep environments separate" className="shadow-none">
Don't mix dev/beta/stable releases. Tag builds so you can correlate spikes
with a rollout and verify fixes quickly.
</Card>
</Cards>
With capture points in each runtime, user identification wired up, and a provider configured, extension monitoring becomes a tight feedback loop: you can spot regressions early, understand which surface area is failing and validate fixes confidently as you ship new versions.

View File

@@ -0,0 +1,167 @@
---
title: PostHog
description: Learn how to setup PostHog as your browser extension monitoring provider.
url: /docs/extension/monitoring/posthog
---
# PostHog
[PostHog](https://posthog.com/) is a product analytics platform that also supports monitoring capabilities like error tracking and session replay. In extensions, it's especially useful when you want to connect “what broke” with “what the user did” right before the issue occurred.
TurboStarter keeps monitoring behind a unified API, so you can route exception captures from your popup, background, and content scripts to PostHog without rewriting the call sites.
<Callout type="warn" title="Prerequisite: PostHog account">
To use PostHog as your monitoring provider, you'll need a PostHog instance. You can use [PostHog Cloud](https://app.posthog.com/signup) or [self-host](https://posthog.com/docs/self-host).
</Callout>
<Callout type="info" title="You can also use it for extension analytics">
PostHog is also supported as an analytics provider for the extension. If you want to track in-extension events, see the [analytics overview](/docs/extension/analytics/overview) and the [PostHog analytics configuration](/docs/extension/analytics/configuration#posthog).
</Callout>
![PostHog banner](/images/docs/web/monitoring/posthog/banner.jpg)
## Configuration
Here you'll configure PostHog as the monitoring provider for your extension so exceptions from the popup, background/service worker, and content scripts show up with enough context to debug.
<Steps>
<Step>
### Create a project
Create a PostHog [project](https://app.posthog.com/project/settings) for your extension. You can do this from the [PostHog dashboard](https://app.posthog.com) via the *New Project* action.
</Step>
<Step>
### Activate PostHog as your monitoring provider
TurboStarter picks the extension monitoring provider through exports in the monitoring package. To route captures to PostHog, export the PostHog implementation from the extension monitoring entrypoint:
```ts title="index.ts"
// [!code word:posthog]
export * from "./posthog";
export * from "./posthog/env";
```
</Step>
<Step>
### Set environment variables
Add your PostHog project key (and host, if you're not using the default cloud region) to your extension env. Set these locally and in whatever build environment produces your extension bundles:
```dotenv title="apps/extension/.env.local"
VITE_POSTHOG_KEY="your-posthog-project-api-key"
VITE_POSTHOG_HOST="https://us.i.posthog.com"
```
</Step>
</Steps>
That's it — load the extension, trigger a test error from the popup/background/content script, and confirm events are arriving in your PostHog project.
![PostHog error](/images/docs/web/monitoring/posthog/error.png)
If you want to go beyond basic capture (session replay, feature flags, richer context), follow PostHog's web/extension guidance.
<Cards>
<Card title="Error tracking" href="https://posthog.com/docs/error-tracking" description="posthog.com" />
<Card title="Web error tracking installation" href="https://posthog.com/docs/error-tracking/installation/web" description="posthog.com" />
</Cards>
## Uploading source maps
**Source maps** map the minified/bundled JavaScript shipped with your extension back to your original source code. Without them, stack traces in PostHog often point at compiled output, which makes debugging much slower.
<Callout>
PostHogs source map flow for web builds relies on injecting metadata into the bundled assets. You must deploy/ship the injected assets, otherwise PostHog cant match captured errors to the uploaded symbol sets.
</Callout>
For extensions built with Vite (which [WXT](https://wxt.dev/) is using under the hood), the high-level flow is:
* generate `.map` files during the production build
* inject PostHog metadata into the built assets
* upload the injected source maps to PostHog
<Steps>
<Step>
### Install the PostHog CLI
Install the CLI globally:
```bash
npm install -g @posthog/cli
```
</Step>
<Step>
### Authenticate the CLI
Authenticate interactively:
```bash
posthog-cli login
```
In CI, you can authenticate with environment variables:
```dotenv
POSTHOG_CLI_HOST="https://us.posthog.com"
POSTHOG_CLI_ENV_ID="your-posthog-project-id"
POSTHOG_CLI_TOKEN="your-personal-api-key"
```
</Step>
<Step>
### Build with source maps enabled
Make sure your extension build outputs source maps by modifying your `wxt.config.ts` file.
```ts title="wxt.config.ts"
import { defineConfig } from "wxt";
export default defineConfig({
/* existing WXT configuration options */
vite: () => ({
build: {
sourcemap: "hidden", // [!code ++] Source map generation must be turned on ("hidden", true, etc.)
},
}),
});
```
After building, you should have `.js` and `.js.map` files in your output directory.
</Step>
<Step>
### Inject PostHog metadata into the built assets
Inject release/chunk metadata so PostHog can associate uploaded maps with the shipped bundles:
```bash
posthog-cli sourcemap inject --directory ./path/to/assets --project my-extension --version 1.2.3
```
</Step>
<Step>
### Upload source maps
Upload the injected source maps to PostHog:
```bash
posthog-cli sourcemap upload --directory ./path/to/assets
```
</Step>
<Step>
### Verify injection and uploads
After deployment, confirm your production bundles include the injected comment (for example `//# chunkId=...`) and verify symbol sets exist in your PostHog project settings.
</Step>
</Steps>
With this in place, PostHog can symbolicate extension errors (popup/options UI, background/service worker, and content scripts) so stack traces point back to your original source files.
<Cards>
<Card title="What are source maps?" href="https://web.dev/articles/source-maps" description="web.dev" />
<Card title="Upload source maps for web" href="https://posthog.com/docs/error-tracking/upload-source-maps/web" description="posthog.com" />
</Cards>

View File

@@ -0,0 +1,149 @@
---
title: Sentry
description: Learn how to setup Sentry as your browser extension monitoring provider.
url: /docs/extension/monitoring/sentry
---
# Sentry
[Sentry](https://sentry.io/) is a popular error monitoring and performance tracking platform. It helps you catch and debug issues by collecting exceptions, stack traces, and helpful context from production.
For browser extensions, that context matters even more: errors can happen in multiple runtimes (popup/options UI, background/service worker, and content scripts). Sentry makes it easier to see what failed and where it happened so you can ship fixes with confidence.
<Callout type="warn" title="Prerequisite: Sentry account">
To use Sentry, create an account and a project first. You can sign up [here](https://sentry.io/signup).
</Callout>
![Sentry banner](/images/docs/web/monitoring/sentry/banner.png)
## Configuration
This section walks you through enabling Sentry for your extension and verifying that errors from the popup, background/service worker, and content scripts are captured reliably.
<Steps>
<Step>
### Create a project
Create a Sentry [project](https://docs.sentry.io/product/projects/) for the extension (JavaScript / browser). You can do this from the Sentry [projects dashboard](https://sentry.io/settings/account/projects/) via the *Create Project* flow.
</Step>
<Step>
### Activate Sentry as your monitoring provider
TurboStarter picks the extension monitoring provider via exports in the monitoring package. To enable Sentry, export the Sentry implementation from the extension monitoring entrypoint:
```ts title="index.ts"
// [!code word:sentry]
export * from "./sentry";
export * from "./sentry/env";
```
If you need to customize behavior, the provider implementation lives under `packages/monitoring/extension/src/providers/sentry`.
</Step>
<Step>
### Set environment variables
From your Sentry project settings, add the DSN and environment to your extension env file (and to any [CI/build step](/docs/extension/publishing/checklist#build-your-app) that produces your extension bundles):
```dotenv title="apps/extension/.env.local"
VITE_SENTRY_DSN="your-sentry-dsn"
VITE_SENTRY_ENVIRONMENT="your-project-environment"
```
</Step>
</Steps>
That's it — load the extension, trigger a test error from the popup/background/content script, and confirm it shows up in your [Sentry dashboard](https://sentry.io/settings/account/projects/).
![Sentry error](/images/docs/web/monitoring/sentry/error.jpg)
For advanced options (sampling, releases, extra context), refer to [Sentry's JavaScript docs](https://docs.sentry.io/platforms/javascript/).
<Cards>
<Card title="Quick Start" href="https://docs.sentry.io/platforms/javascript/" description="docs.sentry.io" />
<Card title="Manual Setup" href="https://docs.sentry.io/platforms/javascript/install/npm/" description="docs.sentry.io" />
</Cards>
## Uploading source maps
**Source maps** map the bundled/minified JavaScript shipped with your extension back to your original source files. Without them, Sentry stack traces often point to compiled output, which makes debugging across popup/background/content-script runtimes much harder.
<Callout>
Generating source maps can expose your source code if `.map` files are publicly accessible. Prefer hidden source maps and/or delete them after upload.
</Callout>
Sentry can automatically provide readable stack traces for errors using source maps, requiring a [Sentry auth token](https://docs.sentry.io/account/auth-tokens/).
<Steps>
<Step>
### Install the Sentry Vite plugin
Install the package `@sentry/vite-plugin` in `apps/extension/package.json` as a dev dependency.
```bash
pnpm i @sentry/vite-plugin -D --filter extension
```
</Step>
<Step>
### Add an auth token for uploads
Create an [auth token in Sentry](https://docs.sentry.io/account/auth-tokens/) and provide it as an environment variable during builds (locally and in your build environment):
```dotenv
SENTRY_AUTH_TOKEN="your-sentry-auth-token"
```
</Step>
<Step>
### Enable source maps and configure the plugin
Enable source map generation in your extension build and add `sentryVitePlugin` **after** your other Vite plugins:
```ts title="wxt.config.ts"
import { defineConfig } from "wxt";
import { sentryVitePlugin } from "@sentry/vite-plugin";
export default defineConfig({
/* existing WXT configuration options */
vite: () => ({
build: {
sourcemap: "hidden", // [!code ++] Source map generation must be turned on ("hidden", true, etc.)
},
plugins: [
sentryVitePlugin({
org: "your-sentry-org",
project: "your-sentry-project",
authToken: process.env.SENTRY_AUTH_TOKEN,
sourcemaps: {
// As you're enabling client source maps, you probably want to delete them after they're uploaded to Sentry.
// Set the appropriate glob pattern for your output folder - some glob examples below:
filesToDeleteAfterUpload: [
"./**/*.map",
".*/**/public/**/*.map",
"./dist/**/client/**/*.map",
],
},
}),
],
}),
});
```
</Step>
<Step>
### Verify uploads with a production build
The Sentry Vite plugin doesn't upload in dev/watch mode. Run a production build, then trigger a test error in the extension and confirm stack traces resolve to your original source.
</Step>
</Steps>
Once this is in place, errors from your extension's compiled bundles (popup/options UI, background/service worker, content scripts) should show **readable stack traces** in Sentry, without shipping source maps to end users.
<Cards>
<Card title="What are source maps?" href="https://web.dev/articles/source-maps" description="web.dev" />
<Card title="Sentry Vite plugin" href="https://docs.sentry.io/platforms/javascript/sourcemaps/uploading/vite/" description="docs.sentry.io" />
</Cards>

View File

@@ -0,0 +1,63 @@
---
title: Organizations/teams
description: Learn how to use organizations/teams/multi-tenancy in TurboStarter extension.
url: /docs/extension/organizations
---
# Organizations/teams
TurboStarter extensions support organizations/teams out of the box by sharing the same authentication session as your web app. The active organization is stored in the session and available to your extension without re-implementing organizations logic.
<Callout type="info" title="Shared session and tenant context">
The extension and web app use a single auth session powered by Better Auth. The session includes tenant context (for example, `activeOrganizationId`). When users sign in, switch organizations, or sign out in the web app, the extension picks up these changes automatically.
Learn more: [Auth → Session](/docs/extension/auth/session).
</Callout>
## How it works
* **No separate auth flow** in the extension. We reuse the web session.
* **Active organization comes from the session** (e.g., `session.activeOrganizationId`).
* **Protected API calls** from the extension include the right cookies, so orgscoped server logic works as expected.
![Shared authentication with organizations in extension](/images/docs/extension/organizations.png)
## Active organization
Use your existing auth client to read the active organization through the `useActiveOrganization` hook.
```tsx title="popup.tsx"
import { authClient } from "~/lib/auth";
export function Popup() {
const organization = authClient.useActiveOrganization();
return <>{organization?.name}</>;
}
```
<Callout title="Switching organizations">
If a user switches organizations in the web app, the extension reflects the change through the shared session on the next interaction. For long-lived views, re-read the session or invalidate related queries when appropriate.
</Callout>
## Do more with organizations
Most organization features live in the web app and are exposed via APIs your extension can call. These guides explain the underlying concepts and server behavior your extension builds upon:
<Cards>
<Card title="Overview" description="Concepts and architecture" href="/docs/web/organizations/overview" />
<Card title="Data model" description="Tables and relationships" href="/docs/web/organizations/data-model" />
<Card title="Active organization" description="How organization context is resolved" href="/docs/web/organizations/active-organization" />
<Card title="RBAC" description="Roles and permissions" href="/docs/web/organizations/rbac" />
<Card title="Invitations" description="Invite teammates and manage members" href="/docs/web/organizations/invitations" />
</Cards>
<Callout>
Looking for the underlying auth setup? Start with [Auth →
Overview](/docs/extension/auth/overview) and [Auth →
Session](/docs/extension/auth/session).
</Callout>

View File

@@ -0,0 +1,162 @@
---
title: Checklist
description: Let's publish your TurboStarter extension to stores!
url: /docs/extension/publishing/checklist
---
# Checklist
When you're ready to publish your TurboStarter extension to stores, follow this checklist.
This process may take a few hours and some trial and error, so buckle up - you're almost there!
<Steps>
<Step>
## Create database instance
**Why it's necessary?**
A production-ready database instance is essential for storing your application's data securely and reliably in the cloud. [PostgreSQL](https://www.postgresql.org/) is the recommended database for TurboStarter due to its robustness, features, and wide support.
**How to do it?**
You have several options for hosting your PostgreSQL database:
* [Supabase](/docs/extension/recipes/supabase) - Provides a fully managed Postgres database with additional features
* [Vercel Postgres](https://vercel.com/storage/postgres) - Serverless SQL database optimized for Vercel deployments
* [Neon](https://neon.tech/) - Serverless Postgres with automatic scaling
* [Turso](https://turso.tech/) - Edge database built on libSQL with global replication
* [DigitalOcean](https://www.digitalocean.com/products/managed-databases) - Managed database clusters with automated failover
Choose a provider based on your needs for:
* Pricing and budget
* Geographic region availability
* Scaling requirements
* Additional features (backups, monitoring, etc.)
</Step>
<Step>
## Migrate database
**Why it's necessary?**
Pushing database migrations ensures that your database schema in the remote database instance is configured to match TurboStarter's requirements. This step is crucial for the application to function correctly.
**How to do it?**
You basically have two possibilities for doing a migration:
<Tabs items={["Using GitHub Actions (recommended)", "Running locally"]}>
<Tab value="Using GitHub Actions (recommended)">
TurboStarter comes with a predefined GitHub Action to handle database migrations. You can find its definition in the `.github/workflows/publish-db.yml` file.
What you need to do is set your `DATABASE_URL` as a [secret for your GitHub repository](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions).
Then, you can run the workflow which will publish the database schema to your remote database instance.
[Check how to run GitHub Actions workflow.](https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow)
</Tab>
<Tab value="Running locally">
You can also run your migrations locally, although this is not recommended for production.
To do so, set the `DATABASE_URL` environment variable to your database URL (that comes from your database provider) in the `.env.local` file and run the following command:
```bash
pnpm with-env pnpm --filter @turbostarter/db db:migrate
```
This command will run the migrations and apply them to your remote database.
[Learn more about database migrations.](/docs/web/database/migrations)
</Tab>
</Tabs>
</Step>
<Step>
## Set up web backend API
**Why it's necessary?**
Setting up the backend is necessary to have a place to store your data and to have other features work properly (e.g. authentication, billing or storage).
**How to do it?**
Please refer to the [web deployment checklist](/docs/web/deployment/checklist) on how to set up and deploy the web app backend to production.
</Step>
<Step>
## Environment variables
**Why it's necessary?**
Setting the correct environment variables is essential for the extension to function correctly. These variables include API keys, database URLs, and other configuration details required for your extension to connect to various services.
**How to do it?**
Use our `.env.example` files to get the correct environment variables for your project. Then add them to your CI/CD provider (e.g. [GitHub Actions](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions)) as a [secret](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions).
</Step>
<Step>
## Build your app
**Why it's necessary?**
Building your extension is necessary to create a standalone extension bundle that can be published to the stores.
**How to do it?**
You basically have two possibilities to build a bundle for your extension:
<Tabs items={["Using GitHub Actions (recommended)", "Running locally"]}>
<Tab value="Using GitHub Actions (recommended)">
TurboStarter comes with a predefined GitHub Action to handle building your extension for submission. You can find its definition in the `.github/workflows/publish-extension.yml` file.
[Check how to run GitHub Actions workflow.](https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow)
This will also save the `.zip` file as an [artifact](https://docs.github.com/en/actions/guides/storing-workflow-data-as-artifacts) of the workflow run, so you can download it from there and submit your extension to stores (if configured).
</Tab>
<Tab value="Running locally">
You can also run your build locally, although this is not recommended for production.
To do it, run the following command:
```bash
pnpm turbo build --filter=extension
```
This will build the extension and package it into a `.zip` file. You can find the output in the `build` folder.
</Tab>
</Tabs>
</Step>
<Step>
## Submit to stores
**Why it's necessary?**
Publishing your extension to the stores is required to make it discoverable and accessible to your users. This is the official distribution channel where users can find, install, and trust your extension.
**How to do it?**
We've prepared dedicated guides for each store that TurboStarter supports out-of-the-box, please refer to the following pages:
<Cards>
<Card title="Chrome Web Store" href="/docs/extension/publishing/chrome" description="Publish your extension to Google Chrome Web Store." />
<Card title="Firefox Add-ons" href="/docs/extension/publishing/firefox" description="Publish your extension to Mozilla Firefox Add-ons." />
<Card title="Edge Add-ons" href="/docs/extension/publishing/edge" description="Publish your extension to Microsoft Edge Add-ons." />
</Cards>
</Step>
</Steps>
That's it! Your extension is now live and accessible to your users, good job! 🎉
<Callout title="Other things to consider">
* Optimize your store listing description, keywords, and other relevant information for the stores.
* Remove the placeholder content in the extension or replace it with your own.
* Update the favicon, scheme, store images, and logo with your own branding.
</Callout>

View File

@@ -0,0 +1,166 @@
---
title: Chrome Web Store
description: Publish your extension to Google Chrome Web Store.
url: /docs/extension/publishing/chrome
---
# Chrome Web Store
[Chrome Web Store](https://chromewebstore.google.com/) is the most popular store for browser extensions, as it makes them available in any Chromium-based browser, including Google Chrome, Edge, Brave, and many others.
To submit your extension to Chrome Web Store, you'll need to complete a few steps. Here, we'll go through them.
<Callout title="Prerequisite" type="warn">
Make sure your extension follows the [guidelines](/docs/extension/marketing) and other requirements to increase your chances of getting approved.
</Callout>
## Developer account
Before you can publish items on the Chrome Web Store, you must register as a CWS developer and pay a one-time registration fee. You must provide a developer email when you create your developer account.
To register, just access the [developer console](https://chrome.google.com/webstore/devconsole). The first time you do this, the following registration screen will appear. First, agree to the developer agreement and policies, then pay the registration fee.
![Chrome registration fee](/images/docs/extension/chrome/fee.png)
Once you pay the registration fee and agree to the terms, your account will be created, and you'll be able to proceed to fill out additional information about it.
![Chrome developer account](/images/docs/extension/chrome/account.png)
There are a few fields that you'll need to fill in:
* **Publisher name**: Appears under the title of each of your extensions. If you are a verified publisher, you can display an official publisher URL instead.
* **Verified email**: Verifying your contact email address is required when you set up a new developer account. It's only displayed under your extensions' contact information. Any notifications will be sent to your Chrome Web Store developer account email.
* **Physical address**: Only items that offer functionality to purchase items, additional features, or subscriptions must include a physical address.
<Card title="Register your developer account" href="https://developer.chrome.com/docs/webstore/register" description="developer.chrome.com" />
## Submission
After registering your developer account, setting it up, and preparing your extension, you're ready to publish it to the store.
You can submit your extension in two ways:
* **Manually**: By uploading your extension's bundle directly to the store.
* **Automatically**: By using GitHub Actions to submit your extension to the stores.
**The first submission must be done manually, while subsequent updates can be submitted automatically.** We'll go through both approaches.
### Manual submission
To manually submit your extension to stores, you will first need to get your extension bundle. If you ran the build step locally, you should already have the `.zip` file in your extension's `build` folder.
If you used GitHub Actions to build your extension, you can find the results in the workflow run. Download the artifacts and save them on your local machine.
Then, use the following steps to upload your item:
1. Go to the [Chrome Web Store Developer Dashboard](https://chrome.google.com/webstore/devconsole/).
2. Sign in to your developer account.
3. Click on the *Add new item* button.
4. Click *Choose file* > *your zip file* > *Upload*. If your item's manifest and other contents are valid, you will see a new item in the dashboard.
![Chrome extension page](/images/docs/extension/chrome/extension-page.png)
After you upload the bundle, you'll need to fill in the extension's details, such as the icons, privacy settings, permissions justification, and other information.
Please refer to the official guides on how to set up your extension's details.
<Cards>
<Card title="Complete your listing information" href="https://developer.chrome.com/docs/webstore/cws-dashboard-listing" description="developer.chrome.com" />
<Card title="Fill out the privacy fields" description="developer.chrome.com" href="https://developer.chrome.com/docs/webstore/cws-dashboard-privacy" />
<Card title="Declare payment and set visibility" description="developer.chrome.com" href="https://developer.chrome.com/docs/webstore/cws-dashboard-distribution" />
</Cards>
### Automated submission
<Callout title="First submission must be done manually" type="warn">
The first submission of your extension to Chrome Web Store must be done manually because you need to provide the store's credentials and extension ID to automation, which will be available only after the first bundle upload.
</Callout>
TurboStarter comes with a pre-configured GitHub Actions workflow to submit your extension to web stores automatically. It's located in the `.github/workflows/publish-extension.yml` file.
What you need to do is fill the environment variables with your store's credentials and extension's details and set them as a [secrets in your Github repository](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions) under correct names:
```yaml title="publish-extension.yml"
env:
CHROME_EXTENSION_ID: ${{ secrets.CHROME_EXTENSION_ID }}
CHROME_CLIENT_ID: ${{ secrets.CHROME_CLIENT_ID }}
CHROME_CLIENT_SECRET: ${{ secrets.CHROME_CLIENT_SECRET }}
CHROME_REFRESH_TOKEN: ${{ secrets.CHROME_REFRESH_TOKEN }}
```
Please refer to the [official guide](https://github.com/PlasmoHQ/bms/blob/main/tokens.md#chrome-web-store-api) to learn how to get these credentials correctly.
That's it! You can [run the workflow](https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow) and it will submit your extension to the Chrome Web Store 🎉
<Callout title="Automated submission to review">
This workflow will also try to send your extension to review, but it's not guaranteed to happen. You need to have all required information filled in your extension's details page to make it possible.
Even then, when you introduce some **breaking change** (e.g. add another permission), you'll need to update your extension store metadata and automatic submit won't be possible.
To opt out of this behavior (and use only automatic uploading to store, but not sending to review) you can set `--chrome-skip-submit-review` flag in the `publish-extension.yml` file for the `wxt submit` command:
```yaml title="publish-extension.yml"
// [!code word:--chrome-skip-submit-review]
- name: 💨 Publish!
run: |
npx wxt submit \
--chrome-zip apps/extension/build/*-chrome.zip --chrome-skip-submit-review
```
Then, your extension bundle will be uploaded to the store, but you will need to send it to review manually.
Check out the [official documentation](https://wxt.dev/api/cli/wxt-submit) for more customization options.
</Callout>
<Cards>
<Card title="Use the Chrome Web Store Publish API" href="https://developer.chrome.com/docs/webstore/using-api" description="developer.chrome.com" />
<Card title="How to generate Google API tokens?" href="https://github.com/PlasmoHQ/chrome-webstore-api/blob/main/token.md" description="github.com" />
</Cards>
## Review
After filling out the information about your item, you are ready to send it to review. Click on *Submit for review* button and confirm that you want to submit your item in the following dialog:
![Chrome submit for review](/images/docs/extension/chrome/send-to-review.png)
The confirmation dialog shown above also lets you control the timing of your item's publishing. If you uncheck the checkbox, your item will **not** be published immediately after its review is complete. Instead, you'll be able to manually publish it at a time of your choosing once the review is complete.
After you submit the item for review, it will undergo a review process. The time for this review depends on the nature of your item. See [Understanding the review process](https://developer.chrome.com/docs/webstore/review-process) for more details.
There are important emails like take down or rejection notifications that are enabled by default. To receive an email notification when your item is published or staged, you can enable notifications on the *Account page*.
![Chrome notifications](/images/docs/extension/chrome/notifications.png)
The review status of your item appears in the [developer dashboard](https://chrome.google.com/webstore/devconsole) next to each item. The status can be one of the following:
* **Published**: Your item is available to all users.
* **Pending**: Your item is under review.
* **Rejected**: Your item was rejected by the store.
* **Taken Down**: Your item was taken down by the store.
![Chrome extension status](/images/docs/extension/chrome/review-status.png)
You'll receive an email notification when the status of your item changes.
<Callout title="Your submission might be rejected" type="error">
If your extension has been determined to violate one or more terms or policies, you will receive an email notification that contains the violation description and instructions on how to rectify it.
If you did not receive an email within a week, check the status of your item. If your item has been rejected, you can see the details on the *Status* tab of your item.
![Chrome extension rejected](/images/docs/extension/chrome/rejection.png)
You'll need to fix the issues and upload a new version of your extension, make sure to follow the [guidelines](/docs/extension/marketing) or check [publishing troubleshooting](/docs/extension/troubleshooting/publishing) for more info.
If you have been informed about a violation and you do not rectify it, your item will be taken down. See [Violation enforcement](https://developer.chrome.com/docs/webstore/review-process#enforcement) for more details.
</Callout>
You can learn more about the review process in the official guides listed below.
<Cards>
<Card title="Chrome Web Store review process" href="https://developer.chrome.com/docs/webstore/review-process" description="developer.chrome.com" />
<Card title="Troubleshooting Chrome Web Store violations" href="https://developer.chrome.com/docs/webstore/troubleshooting" description="developer.chrome.com" />
</Cards>

View File

@@ -0,0 +1,242 @@
---
title: Edge Add-ons
description: Publish your extension to Microsoft Edge Add-ons.
url: /docs/extension/publishing/edge
---
# Edge Add-ons
[Microsoft Edge Add-ons](https://microsoftedge.microsoft.com/addons/) distributes extensions to Microsoft Edge users. If you already have a Chromium-based extension, you can submit it to Edge with minimal changes.
This guide walks you through manual submission and optional automation, aligned with the official process.
<Callout title="Prerequisite" type="warn">
Make sure your extension follows the general [guidelines](/docs/extension/marketing) and the Edge Add-ons developer policies to increase your chances of approval.
</Callout>
## Developer account
To enroll in the Microsoft Edge program you need to have a Microsoft account. If you don't have one, you can create one [here](https://account.microsoft.com/account/signup?signin=1\&ru=https://account.microsoft.com/account/login?loginMethod=email).
![Microsoft account](/images/docs/extension/edge/create-microsoft-account.png)
Next, before you can publish your extension to Edge Add-ons, you need to register your developer account in [Partner Center](https://partner.microsoft.com/dashboard/microsoftedge/public/login?ref=dd). Fill out the required fields and submit the form with *Finish* button. Wait for the email that your account has been verified - you're ready to submit your extension!
![Partner Center](/images/docs/extension/edge/developer-account.png)
<Card title="Register as a Microsoft Edge extension developer" href="https://learn.microsoft.com/en-us/microsoft-edge/extensions/publish/create-dev-account" description="learn.microsoft.com" />
## Submission
After your account is ready and the extension bundle is prepared, you can publish it. There are two paths:
* **Manually**: Upload your `.zip` package through Partner Center.
* **Automatically**: Use CI to upload new versions after the first manual submission.
**The first submission should be done manually.** Subsequent updates can be automated once you have your extension ID and required credentials.
### Manual submission
To manually submit your extension to stores, you will first need to get your extension bundle. If you ran the build step locally, you should already have the .zip file in your extension's build folder.
If you used GitHub Actions to build your extension, you can find the results in the workflow run. Download the artifacts and save them on your local machine.
Then, use the following steps to upload your item:
<Steps>
<Step>
#### Sign in to your developer account
Go to the [Partner Center](https://partner.microsoft.com/dashboard/microsoftedge/public/login?ref=dd) and sign in to your developer account.
</Step>
<Step>
#### Create new extension
Click the *Create new extension* button to start a new submission.
![Create new extension](/images/docs/extension/edge/create.png)
</Step>
<Step>
#### Upload the extension package
The *Extension overview* page shows information for a specific extension:
![Extension overview](/images/docs/extension/edge/upload.png)
To upload your extension package:
1. Click *Packages* in the left sidebar.
2. Drag and drop your `.zip` file or click *Browse your files* to select it.
3. Wait for validation to complete. If it fails, fix any issues and re-upload.
4. Review the extracted extension details and click *Continue*.
</Step>
<Step>
#### Set availability
Choose visibility:
* `Public`: discoverable in the store and via search.
* `Hidden`: not discoverable; accessible via direct listing URL only.
Select markets where the extension is available. You can later add or remove markets; existing users retain access to installed versions.
![Availability](/images/docs/extension/edge/availability.png)
</Step>
<Step>
#### Enter properties
Provide category, privacy policy requirements, privacy policy URL (if applicable), website URL, and support contact.
These are shown to users on the listing and must meet policy requirements.
![Properties](/images/docs/extension/edge/properties.png)
Follow the [official documentation](https://learn.microsoft.com/en-us/microsoft-edge/extensions/publish/publish-extension#step-4-enter-properties-describing-your-extension) for more details.
</Step>
<Step>
#### Add store listing details
Fill in the store listing details for your extension:
* **Display name**: The name shown in the store (from your manifest file).
* **Description**: A detailed description (250-5000 characters) explaining what your extension does and why users should install it.
* **Extension Store logo**: A 300x300 pixel logo representing your extension.
* **Screenshots**: Up to 10 screenshots (640x480 or 1280x800 pixels) showing your extension's functionality.
* **Small/Large promotional tiles**: Optional promotional images for store featuring.
* **YouTube video URL**: Optional promotional video.
* **Search terms**: Keywords to help users discover your extension (up to 21 words total).
You must provide the description and logo for each supported language. Other fields are optional but recommended for better discoverability.
![Store listing details](/images/docs/extension/edge/store-listing.png)
Follow the [official documentation](https://learn.microsoft.com/en-us/microsoft-edge/extensions/publish/publish-extension#step-5-add-store-listing-details-for-your-extension) for detailed requirements and best practices.
</Step>
<Step>
#### Submit for review
Complete the submission by providing testing notes to help certification testers understand your extension.
Click the *Submit* button to open the submission page:
![Submit extension](/images/docs/extension/edge/submit.png)
In the **Notes for certification** text box, provide additional information to help testers properly evaluate your extension. Include any relevant details such as:
* Test account usernames and passwords
* Steps to access hidden or locked features
* Expected differences based on region or user settings
* Information about changes if this is an update
* Any other context testers need to understand your submission
Once you've added your notes, click the *Publish* button to submit your extension for certification.
Your extension will proceed to the certification step, which can take up to seven business days.
After passing certification, your extension will be published to [Microsoft Edge Add-ons](https://microsoftedge.microsoft.com/addons/) and the status in Partner Center will change to "In the Store".
</Step>
</Steps>
<Cards>
<Card title="Publish a Microsoft Edge extension" href="https://learn.microsoft.com/en-us/microsoft-edge/extensions/publish/publish-extension" description="learn.microsoft.com" />
<Card title="Curation and review process for extensions at Microsoft Edge Add-ons" href="https://learn.microsoft.com/en-us/microsoft-edge/extensions/publish/add-ons-curation" description="learn.microsoft.com" />
</Cards>
## Automated submission
<Callout title="First submission must be done manually" type="warn">
The first submission of your extension to Microsoft Edge Add-ons must be done manually because you need to provide the store's credentials and extension ID to automation, which will be available only after the first bundle upload.
</Callout>
TurboStarter comes with a pre-configured GitHub Actions workflow to submit your extension to web stores automatically. It's located in the .github/workflows/publish-extension.yml file.
What you need to do is fill the environment variables with your store's credentials and extension's details and set them as a [secrets in your Github repository](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions) under correct names:
```yaml title="publish-extension.yml"
env:
EDGE_PRODUCT_ID: ${{ secrets.EDGE_PRODUCT_ID }}
EDGE_CLIENT_ID: ${{ secrets.EDGE_CLIENT_ID }}
EDGE_API_KEY: ${{ secrets.EDGE_API_KEY }}
```
Please refer to the [official guide](https://github.com/PlasmoHQ/bms/blob/main/tokens.md#edge-add-ons-api-v11) to learn how to get these credentials correctly.
Once configured, you can manually [trigger the workflow](https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow) to upload the new version to Edge Add-ons 🎉
<Callout title="Automated submission to review">
This workflow will also try to send your extension to review, but it's not guaranteed to happen. You need to have all required information filled in your extension's details page to make it possible.
Even then, when you introduce some **breaking change** (e.g. add another permission), you'll need to update your extension store metadata and automatic submit won't be possible.
To opt out of this behavior (and use only automatic uploading to store, but not sending to review) you can set `--edge-skip-submit-review` flag in the `publish-extension.yml` file for the `wxt submit` command:
```yaml title="publish-extension.yml"
// [!code word:--edge-skip-submit-review]
- name: 💨 Publish!
run: |
npx wxt submit \
--edge-zip apps/extension/build/*-chrome.zip --edge-skip-submit-review
```
Then, your extension bundle will be uploaded to the store, but you will need to send it to review manually.
Check out the [official documentation](https://wxt.dev/api/cli/wxt-submit) for more customization options.
</Callout>
<Cards>
<Card title="Alternative ways to distribute an extension" href="https://learn.microsoft.com/en-us/microsoft-edge/extensions/developer-guide/alternate-distribution-options" description="learn.microsoft.com" />
<Card title="REST API for updating an extension" href="https://learn.microsoft.com/en-us/microsoft-edge/extensions/update/api/using-addons-api?tabs=v1-1" description="learn.microsoft.com" />
</Cards>
## Review
After you submit your extension, it enters Microsoft's certification and publishing pipeline.
1. Preprocessing
* Uploaded packages are queued and scanned. If errors are detected during preprocessing, you'll see a message and must resolve issues before re-uploading.
2. Certification
* Security tests: packages are checked for viruses and malware.
* Content compliance: human review of your listing and content for policy adherence.
3. Release and publishing
* If you selected publish immediately, publishing begins right away; otherwise schedule/hold options apply.
* While publishing, the submission status page shows rollout details. When complete, the status changes from "Publishing" to "In the Store".
4. Edge Add-ons curation and ranking
* Discovery is influenced by quality, relevancy (name, description, popularity, user experience), and popularity (ratings and averages). Security and policy compliance are verified per the developer policies.
Microsoft may also perform spot checks after publishing to ensure ongoing compliance.
The review status of your item appears in the [Partner Center](https://partner.microsoft.com/dashboard/microsoftedge/public/login?ref=dd) under the *Overview* page of your item.
![Edge extension review status](/images/docs/extension/edge/review-status.png)
You'll receive an email notification when the status of your item changes.
<Callout title="Your submission might be rejected" type="error">
If your extension has been determined to violate one or more terms or policies, you will receive an email notification that contains the violation description and instructions on how to rectify it.
![Rejection email](/images/docs/extension/edge/rejection-email.png)
You can also check the reason behind the rejection on the *Certification report* page of your item.
![Certification report](/images/docs/extension/edge/certification-report.png)
You'll need to fix the issues and upload a new version of your extension. Make sure to follow the [guidelines](/docs/extension/marketing) or check [publishing troubleshooting](/docs/extension/troubleshooting/publishing) for more info.
</Callout>
You can learn more about the review process in the official guides listed below.
<Cards>
<Card title="Microsoft Edge Add-ons developer policies" href="https://learn.microsoft.com/en-us/legal/microsoft-edge/extensions/developer-policies" description="learn.microsoft.com" />
<Card title="The app certification process for add-on" href="https://learn.microsoft.com/en-us/windows/apps/publish/publish-your-app/add-on/app-certification-process" description="learn.microsoft.com" />
<Card title="Curation and review process for extensions at Microsoft Edge Add-ons" href="https://learn.microsoft.com/en-us/microsoft-edge/extensions/publish/add-ons-curation" description="learn.microsoft.com" />
</Cards>

View File

@@ -0,0 +1,218 @@
---
title: Firefox Add-ons
description: Publish your extension to Mozilla Firefox Add-ons.
url: /docs/extension/publishing/firefox
---
# Firefox Add-ons
Mozilla Firefox doesn't share extensions with [Google Chrome](/docs/extension/publishing/chrome), so you'll need to publish your extension to it separately.
Here, we'll go through the process of publishing an extension to [Firefox Add-ons](https://addons.mozilla.org/).
<Callout title="Prerequisite" type="warn">
Make sure your extension follows the [guidelines](/docs/extension/marketing) and other requirements to increase your chances of getting approved.
</Callout>
## Developer account
Before you can publish items on Firefox Add-ons, you must register a developer account. In comparison to the Chrome Web Store, Firefox Add-ons doesn't require a registration fee.
To register, go to [addons.mozilla.org](https://addons.mozilla.org/) and click on the *Register* button.
![Mozilla registration](/images/docs/extension/firefox/portal.png)
It's important to set at least a display name on your profile to increase transparency with users, add-on reviewers, and the greater community.
You can do it in the *Edit My Profile* section:
![Mozilla profile](/images/docs/extension/firefox/profile.png)
<Card title="Developer accounts" href="https://extensionworkshop.com/documentation/publish/developer-accounts/" description="extensionworkshop.com" />
## Submission
After registering your developer account, setting it up, and preparing your extension, you're ready to publish it to the store.
You can submit your extension in two ways:
* **Manually**: By uploading your extension's bundle directly to the store.
* **Automatically**: By using GitHub Actions to submit your extension to the stores.
**The first submission must be done manually, while subsequent updates can be submitted automatically.** We'll go through both approaches.
### Manual submission
To manually submit your extension to stores, you will first need to get your extension bundle. If you ran the build step locally, you should already have the `.zip` file in your extension's `build` folder.
If you used GitHub Actions to build your extension, you can find the results in the workflow run. Download the artifacts and save them on your local machine.
Then, use the following steps to upload your item:
<Steps>
<Step>
#### Sign in to your developer account
Go to the [Add-ons Developer Hub](https://addons.mozilla.org/developers/) and sign in to your developer account.
</Step>
<Step>
#### Choose distribution method
You should reach the following page:
![Mozilla distribution](/images/docs/extension/firefox/distribution.png)
Here, you have two ways of distributing your extension:
* **On this site**, if you want your add-on listed on AMO (Add-ons Manager).
* **On your own**, if you plan to distribute the add-on yourself and don't want it listed on AMO.
We recommend going with the first option, as it will allow you to reach more users and get more feedback. If you decide to go with the second option, please refer to the [official documentation](https://extensionworkshop.com/documentation/publish/self-distribution/) for more details.
</Step>
<Step>
#### Submit your extension
On the next page, click on *Select file* and choose your extension's `.zip` bundle.
![Mozilla upload](/images/docs/extension/firefox/upload.png)
Once you upload the bundle, the validator checks the add-on for issues and the page updates:
![Mozilla validation](/images/docs/extension/firefox/validation.png)
If your add-on passes all the checks, you can proceed to the next step.
<Callout type="warn">
You may receive a message that you only have warnings. It's advisable to address these warnings, particularly those flagged as security or privacy issues, as they may result in your add-on failing review. However, **you can continue with the submission**.
</Callout>
If the validation fails, you'll need to address the issues and upload a new version of your add-on.
</Step>
<Step>
#### Submit source code (if needed)
You'll need to indicate whether you need to provide the source code of your extension:
![Mozilla source code](/images/docs/extension/firefox/source-code.png)
If you select *Yes*, a section displays describing what you need to submit. Click *Browse* and locate and upload your source code package. See [Source code submission](https://extensionworkshop.com/documentation/publish/source-code-submission/) for more information.
<Callout type="warn">
You may receive a message that you only have warnings. It's advisable to address these warnings, particularly those flagged as security or privacy issues, as they may result in your add-on failing review. However, **you can continue with the submission**.
</Callout>
If the validation fails, you'll need to address the issues and upload a new version of your add-on.
</Step>
<Step>
#### Add metadata
On the next page, you'll need to provide the following additional information about your extension:
![Mozilla additional information](/images/docs/extension/firefox/additional-info.png)
* **Name**: Your add-on's name.
* **Add-on URL**: The URL for your add-on on AMO. A URL is automatically assigned based on your add-on's name. To change this, click Edit. The URL must be unique. You will be warned if another add-on is using your chosen URL, and you must enter a different one.
* **Summary**: A useful and descriptive short summary of your add-on.
* **Description**: A longer description that provides users with details of the extension's features and functionality.
* **This add-on is experimental**: Indicate if your add-on is experimental or otherwise not ready for general use. The add-on will be listed but with reduced visibility. You can remove this flag when your add-on is ready for general use.
* **This add-on requires payment, non-free services or software, or additional hardware**: Indicate if your add-on requires users to make an additional purchase for it to work fully.
* **Select up to 2 Firefox categories for this add-on**: Select categories that describe your add-on.
* **Select up to 2 Firefox for Android categories for this add-on**: Select categories that describe your add-on.
* **Support email and Support website**: Provide an email address and website where users can get in touch when they have questions, issues, or compliments.
* **License**: Select the appropriate license for your add-on. Click Details to learn more about each license.
* **This add-on has a privacy policy**: If any data is being transmitted from the user's device, a privacy policy explaining what is being sent and how it's used is required. Check this box and provide the privacy policy.
* **Notes for Reviewers**: Provide information to assist the AMO reviewer, such as login details for a dummy account, source code information, or similar.
</Step>
<Step>
#### Finalize the process
Once you're ready, click on the *Submit Version* button.
![Mozilla submit](/images/docs/extension/firefox/submit.png)
You can still edit your add-on's details from the dedicated page after submission.
</Step>
</Steps>
<Cards>
<Card title="Resources for publishers" href="https://extensionworkshop.com/documentation/manage/resources-for-publishers/" description="extensionworkshop.com" />
<Card title="Source code submission" href="https://extensionworkshop.com/documentation/publish/source-code-submission/" description="extensionworkshop.com" />
</Cards>
### Automated submission
<Callout title="First submission must be done manually" type="warn">
The first submission of your extension to Firefox Add-ons must be done manually because you need to provide the store's credentials and extension ID to automation, which will be available only after the first bundle upload.
</Callout>
TurboStarter comes with a pre-configured GitHub Actions workflow to submit your extension to web stores automatically. It's located in the `.github/workflows/publish-extension.yml` file.
What you need to do is fill the environment variables with your store's credentials and extension's details and set them as a [secrets in your Github repository](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions) under correct names:
```yaml title="publish-extension.yml"
env:
FIREFOX_EXTENSION_ID: ${{ secrets.FIREFOX_EXTENSION_ID }}
FIREFOX_JWT_ISSUER: ${{ secrets.FIREFOX_JWT_ISSUER }}
FIREFOX_JWT_SECRET: ${{ secrets.FIREFOX_JWT_SECRET }}
```
Please refer to the [official guide](https://github.com/PlasmoHQ/bms/blob/main/tokens.md#firefox-add-ons-api) to learn how to get these credentials correctly.
That's it! You can [run the workflow](https://docs.github.com/en/actions/managing-workflow-runs-and-deployments/managing-workflow-runs/manually-running-a-workflow) and it will submit your extension to the Firefox Add-ons 🎉
<Callout title="Automated submission to review">
This workflow will also try to send your extension to review, but it's not guaranteed to happen. You need to have all required information filled in your extension's details page to make it possible.
Even then, when you introduce some **breaking change** (e.g., add another permission), you'll need to update your extension store metadata and automatic submission won't be possible.
</Callout>
<Cards>
<Card title="A new API for submitting and updating add-ons" href="https://blog.mozilla.org/addons/2022/03/17/new-api-for-submitting-and-updating-add-ons/" description="blog.mozilla.org" />
<Card title="Mozilla API keys" href="https://addons.mozilla.org/en-US/developers/addon/api/key/" description="addons.mozilla.org" />
</Cards>
## Review
Once you submit your extension bundle, it's automatically sent to review and will undergo a review process. The time for this review depends on the nature of your item.
The add-on review process includes the following phases:
1. **Automatic Review**: Upon upload, the add-on undergoes several automatic validation steps to ensure its general safety.
2. **Content Review**: Shortly after submission, a human reviewer inspects the add-on to ensure that the listing adheres to content review guidelines, including metadata such as the add-on name and description.
3. **Technical Code Review**: The add-on's source code is examined to ensure compliance with review policies.
4. **Basic Functionality Testing**: After the source code is verified as safe, the add-on undergoes basic functionality testing to confirm it operates as described.
There are important emails like takedown or rejection notifications that are enabled by default. To receive an email notification when your item is published or staged, you can enable notifications in the *Account Settings*.
![Mozilla notifications](/images/docs/extension/firefox/notifications.png)
The review status of your item appears in the [developer hub](https://addons.mozilla.org/en-US/firefox/) next to each item.
![Mozilla review status](/images/docs/extension/firefox/review-status.png)
You'll receive an email notification when the status of your item changes.
<Callout title="Your submission might be rejected" type="error">
If your extension has been determined to violate one or more terms or policies, you will receive an email notification that contains the violation description and instructions on how to rectify it.
You can also check the reason behind the rejection on the *Status* page of your item.
![Mozilla extension rejected](/images/docs/extension/firefox/rejection.png)
You'll need to fix the issues and upload a new version of your extension. Make sure to follow the [guidelines](/docs/extension/marketing) or check [publishing troubleshooting](/docs/extension/troubleshooting/publishing) for more info.
</Callout>
You can learn more about the review process in the official guides listed below.
<Cards>
<Card title="Add-ons/Reviewers/Guide/Reviewing" href="https://wiki.mozilla.org/Add-ons/Reviewers/Guide/Reviewing" description="wiki.mozilla.org" />
<Card title="Add-ons/Reviewers/Content Review Guidelines" href="https://wiki.mozilla.org/Add-ons/Reviewers/Content_Review_Guidelines" description="wiki.mozilla.org" />
</Cards>

View File

@@ -0,0 +1,28 @@
---
title: Updates
description: Learn how to update your published extension.
url: /docs/extension/publishing/updates
---
# Updates
After publishing your extension to the stores, you can release updates to deliver new features and bug fixes to your users.
TurboStarter provides a ready-to-use process for updating your extensions. Let's quickly review how it works.
## Uploading a new version
The recommended way to update your extension is to submit a new version to the stores. This method is the most reliable, although it may take some time for the new version to be approved and become available to users.
To submit a new version, simply update the version number in your `package.json` file:
```json title="package.json"
{
...
"version": "1.0.0", // [!code --]
"version": "1.0.1", // [!code ++]
...
}
```
Next, follow the exact same steps as [when you initially published your extension](/docs/extension/publishing/checklist). When submitting your extension for review, be sure to provide release notes describing the new version.

View File

@@ -0,0 +1,221 @@
---
title: Supabase
description: Learn how to set up Supabase as the database (and optional storage) provider for your TurboStarter project.
url: /docs/extension/recipes/supabase
---
# Supabase
[Supabase](https://supabase.com) is an open-source backend platform built on top of PostgreSQL that provides a managed database, storage, and other features out of the box.
You can adopt Supabase incrementally - start with just the pieces you need (for example, database only, or database + storage) and add more features over time. There's no requirement to integrate everything at once.
In this guide, we'll walk you through the process of setting up Supabase as a provider for your TurboStarter project. This could include using it as a [database](https://supabase.com/docs/guides/database), [storage](https://supabase.com/docs/guides/storage), [edge runtime for your API](https://supabase.com/docs/guides/functions) and more.
## Prerequisites
Before you start, make sure you have:
* **TurboStarter project** cloned locally with dependencies installed (you can use our [CLI](/docs/web/cli) to create a new project in seconds)
* **Supabase account** - you can create one at [supabase.com](https://supabase.com/sign-up)
* Basic familiarity with the core database docs:
* [Database overview](/docs/web/database/overview)
* [Migrations](/docs/web/database/migrations)
* [Database client](/docs/web/database/client)
<Steps>
<Step>
## Create a new Supabase project
1. Go to the [Supabase dashboard](https://supabase.com).
2. Create a **new project** (choose a strong database password and a region close to your users).
3. Supabase will automatically provision a **PostgreSQL database** for you.
![Create a new Supabase project](/images/docs/web/recipes/supabase/create-project.png)
Optionally, you can customize the **Security options** by choosing the **Only Connection String** option - it will opt out of autogenerating API for tables inside your database. It's not needed for TurboStarter setup, but of course you can still leverage it for your custom use-cases.
![Security options](/images/docs/web/recipes/supabase/security-options.png)
Once the project is ready, you can fetch the connection string.
</Step>
<Step>
## Get the database connection string
In the Supabase dashboard:
1. Open your project.
2. Click on the **Connect** button at the top.
3. Locate the **connection string** for your chosen ORM (it will be under the **ORMs** tab).
![Connect application](/images/docs/web/recipes/supabase/connect-app.png)
Copy this value - you'll use it as your `DATABASE_URL`.
<Callout title="Replace password placeholder" type="warn">
In your Supabase connection string, you can see a placeholder like `[YOUR-PASSWORD]`. Make sure to replace this with the actual password you set when creating your Supabase project.
</Callout>
</Step>
<Step>
## Configure environment variables
TurboStarter reads database connection settings from the **root** `.env.local` file and uses them inside the `@turbostarter/db` package.
Create (or update) the `.env.local` file in the **monorepo root**:
```dotenv title=".env.local"
DATABASE_URL="postgres://postgres.[YOUR-PROJECT-REF]:[YOUR-PASSWORD]@aws-0-[aws-region].pooler.supabase.com:6543/postgres?pgbouncer=true&connection_limit=1"
```
Replace:
* `YOUR-PROJECT-REF` with your Supabase project ref
* `YOUR-PASSWORD` with the database password you set when creating the project
* `aws-region` with the region shown in the Supabase connection string
<Callout>
These variables are validated in the `@turbostarter/db` package and used to create Drizzle client for your database.
</Callout>
For more background on how `DATABASE_URL` is used, see [Database overview](/docs/web/database/overview).
</Step>
<Step>
## Setup your Supabase database
With `DATABASE_URL` now pointing to Supabase, you can apply the existing TurboStarter schema to your Supabase database.
From the monorepo root, run:
```bash
pnpm with-env pnpm --filter @turbostarter/db db:migrate
```
This will:
* Use your Supabase `DATABASE_URL` from `.env.local`
* Run all pending SQL migrations from `packages/db/migrations`
* Create the full TurboStarter schema (users, billing, demo tables, etc.) in Supabase
If you're actively iterating on the schema, you can generate new migrations and apply them as described in [Migrations](/docs/web/database/migrations).
<Callout title="Seeding your database" type="info">
After running your migrations, you may want to seed your database with initial data (such as demo users or organizations). You can do this by running the following command:
```bash
pnpm with-env pnpm turbo db:seed
```
This will populate your Supabase database with some example data you can use to test your application.
</Callout>
</Step>
<Step>
## Use Supabase Storage as S3-compatible storage
TurboStarter's storage layer is designed to work seamlessly with **any S3-compatible provider**. In this section, we'll show how to use [Supabase Storage](/docs/web/storage/overview) as your application's file storage back-end.
Supabase Storage provides a simple, S3-compatible API and is a great choice if you're already using Supabase for your database.
### Create a storage bucket
1. In the Supabase dashboard, go to **Storage → Buckets**.
2. Click **Create bucket** (name it whatever you want, for example `avatars` or `uploads`).
3. Adjust settings based on your needs (e.g. limit the maximum file size, specify the allowed file types, etc.)
![Create a new bucket](/images/docs/web/recipes/supabase/create-bucket.png)
You can create multiple buckets (for documents, images, videos, etc.) if needed.
### Generate S3 access keys in Supabase dashboard
1. Go to **Storage → S3 → Access keys**.
2. Click **New access key**.
3. Give it a descriptive name and create the key.
4. Copy the **Access key ID** and **Secret access key** to use in your application.
![Generate S3 access keys](/images/docs/web/recipes/supabase/s3-keys.png)
### Configure S3 environment variables for Supabase Storage
In your weba application's `.env.local`, add (or update) the S3 configuration used by TurboStarter's storage layer:
```dotenv title=".env.local"
S3_REGION="us-east-1"
S3_BUCKET="avatars"
S3_ENDPOINT="https://[YOUR-PROJECT-REF].supabase.co/storage/v1/s3"
S3_ACCESS_KEY_ID="your-access-key-id"
S3_SECRET_ACCESS_KEY="your-secret-access-key"
```
These variables integrate directly with the storage configuration described in:
* [Storage overview](/docs/web/storage/overview)
* [Storage configuration](/docs/web/storage/configuration)
Once set, existing TurboStarter file upload flows (e.g. user avatars, organization logos) will use Supabase Storage via presigned URLs.
</Step>
<Step>
## Run your API on Supabase Edge Functions
As we're using a [Hono](https://hono.dev) as our API server, you can deploy it as a Supabase Edge Function so it runs close to your users.
At a high level:
1. Install the [Supabase CLI](https://supabase.com/docs/guides/cli) and initialize a Supabase project locally with `supabase init`.
2. Create a new [Edge Function](https://supabase.com/docs/guides/functions/quickstart) (for example `hono-backend`) with `supabase functions new hono-backend`.
3. Inside the generated function (for example `supabase/functions/hono-backend/index.ts`), set up a basic Hono app and export it via `Deno.serve(app.fetch)`:
```ts
import { Hono } from "jsr:@hono/hono";
// change this to your function name
const functionName = "hono-backend";
const app = new Hono().basePath(`/${functionName}`);
app.get("/hello", (c) => c.text("Hello from hono-server!"));
Deno.serve(app.fetch);
```
4. Run the function locally with `supabase start` and `supabase functions serve --no-verify-jwt`, then call it from your TurboStarter app using the local or deployed function URL.
5. When you're ready, deploy the function with `supabase functions deploy` (or `supabase functions deploy hono-backend`) and manage it using the Supabase dashboard, as described in the [Supabase Edge Functions docs](https://supabase.com/docs/guides/functions).
This is entirely optional, but it's a great fit for lightweight APIs, webhooks, and other serverless logic you want to run alongside your Supabase project.
</Step>
<Step>
## Explore additional Supabase features
Supabase is a full Postgres development platform, so beyond the database and storage pieces wired up above you can gradually add more features as your app grows ([see the Supabase homepage](https://supabase.com/) for an overview).
Some features that fit especially well with TurboStarter's design are:
* [Realtime](https://supabase.com/docs/guides/realtime) - built on [Postgres replication](https://www.postgresql.org/docs/current/runtime-config-replication.html), so you can stream changes from your existing TurboStarter tables (inserts, updates, deletes) into live UIs without changing how you manage schema or RLS. You still define tables and policies via `@turbostarter/db`, and opt into Realtime on top.
* [Vector](https://supabase.com/docs/guides/vector) - powered by the [pgvector](https://github.com/pgvector/pgvector) extension and stored in regular Postgres tables, making it easy to integrate semantic search or AI features while keeping everything in the same migrations and Drizzle models you already use in TurboStarter. We're using it extensively in our dedicated [AI Kit](/ai).
* [Cron](https://supabase.com/docs/guides/functions/cron) - enables you to schedule background jobs and periodic tasks with [pg\_cron](https://github.com/citusdata/pg_cron). You can define cron jobs for things like scheduled database cleanups, sending emails, report generation, or any recurring logic, all managed alongside your TurboStarter app with full Postgres integration.
Because these features are all layered on top of Postgres, you can introduce them incrementally and keep managing everything through your familiar workflow.
</Step>
<Step>
## Start the development server
With the database and other services configured to use Supabase, you can start TurboStarter as usual from the monorepo root:
```bash
pnpm dev
```
TurboStarter will now:
* Use **Supabase Postgres** as your database through `DATABASE_URL`
* Use **Supabase Storage** as your file storage through the S3-compatible endpoint
* Leverage **Supabase Edge Functions** (for example, with Hono) for your serverless backend
</Step>
</Steps>
That's it! You can now start building your application with Supabase as your main provider. Explore the [Supabase documentation](https://supabase.com/docs) for more features and best practices.

View File

@@ -0,0 +1,82 @@
---
title: Tech Stack
description: A detailed look at the technical details.
url: /docs/extension/stack
---
# Tech Stack
## Turborepo
[Turborepo](https://turbo.build/) is a monorepo tool that helps you manage your project's dependencies and scripts. We chose a monorepo setup to make it easier to manage the structure of different features and enable code sharing between different packages.
<Card href="https://turbo.build/" title="Turborepo - Make Ship Happen" description="turbo.build" icon={<Turborepo />} />
## WXT (Vite)
> It's like Next.js for browser extensions.
[WXT](https://www.wxt.dev/) is a very lightweight and powerful framework (based on [Vite](https://vite.dev/)) for building browser extensions using most popular frontend tools. It provides a modern development experience with features like hot module reloading, TypeScript support, and automatic manifest generation.
WXT simplifies the process of creating cross-browser extensions, allowing you to focus on your extension's functionality rather than boilerplate setup.
<Cards>
<Card href="https://www.wxt.dev/" title="WXT" description="wxt.dev" icon={<Wxt />} />
<Card href="https://www.vite.dev/" title="Vite" description="vite.dev" icon={<Vite />} />
</Cards>
## React
[React](https://reactjs.org/) is a JavaScript library for building user interfaces. It's the core technology we use for creating the UI of our browser extension, allowing for efficient updates and rendering of components.
<Card href="https://reactjs.org/" title="React" description="reactjs.org" icon={<React />} />
## Tailwind CSS
[Tailwind CSS](https://tailwindcss.com) is a utility-first CSS framework that helps you build custom designs without writing any CSS. We also use [Radix UI](https://radix-ui.com) for our headless components library and [shadcn/ui](https://ui.shadcn.com) which enables you to generate pre-designed components with a single command.
<Cards className="grid-cols-2 sm:grid-cols-3">
<Card href="https://tailwindcss.com" title="Tailwind CSS" description="tailwindcss.com" icon={<Tailwind />} />
<Card href="https://radix-ui.com" title="Radix UI" description="radix-ui.com" icon={<Radix />} />
<Card href="https://ui.shadcn.com" title="shadcn/ui" description="ui.shadcn.com" icon={<Shadcn />} />
</Cards>
## Hono & React Query
[Hono](https://hono.dev) is a small, simple, and ultrafast web framework for the edge. It provides tools to help you build APIs and web applications faster. It includes an RPC client for making type-safe function calls from the frontend. We use Hono to build our serverless API endpoints.
To make data fetching and caching from our API easy and reliable, we pair Hono with [React Query](https://tanstack.com/query/latest). It helps manage asynchronous data, caching, and state synchronization between the client and backend, delivering a fast and seamless UX.
<Cards>
<Card href="https://hono.dev" title="Hono" description="hono.dev" icon={<Hono />} />
<Card
href="https://tanstack.com/query/latest"
title="React Query"
description="tanstack.com"
icon={
<img src="/images/icons/tanstack.png" alt="" width={32} height={32} />
}
/>
</Cards>
## Better Auth
[Better Auth](https://www.better-auth.com) is a modern authentication library for fullstack applications. It provides ready-to-use snippets for features like email/password login, magic links, OAuth providers, and more. We use Better Auth to handle all authentication flows in our application.
<Card href="https://www.better-auth.com" title="Better Auth" description="better-auth.com" icon={<BetterAuth />} />
## Drizzle
[Drizzle](https://orm.drizzle.team/) is a super fast [ORM](https://orm.drizzle.team/docs/overview) (Object-Relational Mapping) tool for databases. It helps manage databases, generate TypeScript types from your schema, and run queries in a fully type-safe way.
We use [PostgreSQL](https://www.postgresql.org) as our default database, but thanks to Drizzle's flexibility, you can easily switch to MySQL, SQLite or any [other supported database](https://orm.drizzle.team/docs/connect-overview) by updating a few configuration lines.
<Cards>
<Card href="https://orm.drizzle.team/" title="Drizzle" description="orm.drizzle.team" icon={<Drizzle />} />
<Card href="https://www.postgresql.org" title="PostgreSQL" description="postgresql.org" icon={<Postgres />} />
</Cards>

View File

@@ -0,0 +1,63 @@
---
title: Background service worker
description: Configure your extension's background service worker.
url: /docs/extension/structure/background
---
# Background service worker
An extension's service worker is a powerful script that runs in the background, separate from other parts of the extension. It's loaded when it is needed, and unloaded when it goes dormant.
Once loaded, an extension service worker generally runs as long as it is actively receiving events, though it [can shut down](https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/lifecycle#idle-shutdown). Like its web counterpart, an extension service worker cannot access the DOM, though you can use it if needed with [offscreen documents](https://developer.chrome.com/docs/extensions/reference/api/offscreen).
Extension service workers are more than network proxies (as web service workers are often described), they run in a separate [service worker context](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers). For example, when in this context, you no longer need to worry about CORS and can fetch resources from any origin.
In addition to the [standard service worker events](https://developer.mozilla.org/docs/Web/API/ServiceWorkerGlobalScope#events), they also respond to extension events such as navigating to a new page, clicking a notification, or closing a tab. They're also registered and updated differently from web service workers.
**It's common to offload heavy computation to the background service worker**, so you should always try to do resouce-expensive operations there and send results using [Messages API](/docs/extension/structure/messaging) to other parts of the extension.
Code for the background service worker is located at `src/app/background` directory - you need to use `defineBackground` within `index.ts` file inside to allow WXT to include your script in the build.
```ts title="src/app/background/index.ts"
import { defineBackground } from "wxt/sandbox";
const main = () => {
console.log(
"Background service worker is running! Edit `src/app/background` and save to reload.",
);
};
export default defineBackground(main);
```
To see the service worker in action, reload the extension, then open its "Service Worker inspector":
![Service Worker inspector](/images/docs/extension/structure/sw-inspector.png)
You should see what we've logged in the console:
![Service Worker console](/images/docs/extension/structure/sw-log.png)
To communicate with the service worker from other parts of the extension, you can use the [Messaging API](/docs/extension/structure/messaging).
## Persisting state
<Callout>
Service workers in `dev` mode always remain in `active` state.
</Callout>
The worker becomes idle after a few seconds of inactivity, and the browser will kill its process entirely after 5 minutes. This means all state (variables, etc.) is lost unless you use a storage engine.
The simplest way to persist your background service worker's state is to use the [storage API](/docs/extension/structure/storage).
The more advanced way is to send the state to a remote database via our [backend API](/docs/extension/api/overview).
<Cards>
<Card title="Using service workers" href="https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers" description="developer.mozilla.org" />
<Card title="Migrate to a service worker" href="https://developer.chrome.com/docs/extensions/develop/migrate/to-service-workers" description="developer.chrome.com" />
<Card title="Extension service worker basics" href="https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/basics" description="developer.chrome.com" />
<Card title="The extension service worker lifecycle" href="https://developer.chrome.com/docs/extensions/develop/concepts/service-workers/lifecycle" description="developer.chrome.com" />
</Cards>

View File

@@ -0,0 +1,94 @@
---
title: Content scripts
description: Learn more about content scripts.
url: /docs/extension/structure/content-scripts
---
# Content scripts
Content scripts run in the context of web pages in an isolated world. This allows multiple content scripts from various extensions to coexist without conflicting with each other's execution and to stay isolated from the page's JavaScript.
A script that ends with `.ts` will not have front-end runtime (e.g. react) bundled with it and won't be treated as a ui script, while a script that ends in `.tsx` will be.
There are many use cases for content scripts:
* Injecting a custom stylesheet into the page
* Scraping data from the current web page
* Selecting, finding, and styling elements from the current web page
* Injecting UI elements into current web page
Code for the content scripts is located in `src/app/content` directory - you need to define `.ts` or `.tsx` file inside and use `defineContentScript` to allow WXT to include your script in the build.
```ts title="src/app/content/index.ts"
export default defineContentScript({
matches: ["<all_urls>"],
async main(ctx) {
console.log(
"Content script is running! Edit `app/content` and save to reload.",
);
},
});
```
Reload your extension, open a web page, then open its inspector:
![Content Script](/images/docs/extension/structure/content-script.png)
To learn more about content scripts, e.g. how to configure only specific pages to load content scripts, how to inject them into `window` object or how to fetch data inside, please check [the official documentation](https://wxt.dev/guide/essentials/content-scripts.html).
## UI scripts
WXT has first-class support for mounting React components into the current webpage. This feature is called content scripts UI (CSUI).
![CSUI](/images/docs/extension/structure/csui.png)
An extension can have as many CSUI as needed, with each CSUI targeting a group of webpages or a specific webpage.
To get started with CSUI, create a `.tsx` file in `src/app/content` directory and use `defineContentScript` allow WXT to include your script in the build and mount your component into the current webpage:
```tsx title="src/app/content/index.tsx"
const ContentScriptUI = () => {
return (
<Button onClick={() => alert("This is injected UI!")}>
Content script UI
</Button>
);
};
export default defineContentScript({
matches: ["<all_urls>"],
cssInjectionMode: "ui",
async main(ctx) {
const ui = await createShadowRootUi(ctx, {
name: "turbostarter-extension",
position: "overlay",
anchor: "body",
onMount: (container) => {
const app = document.createElement("div");
container.append(app);
const root = ReactDOM.createRoot(app);
root.render(<ContentScriptUI />);
return root;
},
onRemove: (root) => {
root?.unmount();
},
});
ui.mount();
},
});
export default ContentScriptUI;
```
<Callout title="File extensions matters!" type="warn">
The `.tsx` extension is essential to differentiate between Content Scripts UI and regular Content Scripts. Make sure to check if you're using appropriate type of content script for your use case.
</Callout>
To learn more about content scripts UI, e.g. how to inject custom styles, fonts or the whole lifecycle of a component, please check [the official documentation](https://wxt.dev/guide/essentials/content-scripts.html#ui).
<Callout title="How does it work?">
Under the hood, the component is wrapped inside the component that implements the Shadow DOM technique, together with many helpful features. This isolation technique prevents the web page's style from affecting your component's styling and vice-versa.
[Read more about the lifecycle of CSUI](https://docs.plasmo.com/framework/content-scripts-ui/life-cycle)
</Callout>

View File

@@ -0,0 +1,95 @@
---
title: Messaging
description: Communicate between your extension's components.
url: /docs/extension/structure/messaging
---
# Messaging
Messaging API makes communication between different parts of your extension easy. To make it simple and scalable, we're leveraging `@webext-core/messaging` library.
It provides a declarative, type-safe, functional, promise-based API for sending, relaying, and receiving messages between your extension components.
## Handling messages
Based on our convention, we implemented a little abstraction on top of `@webext-core/messaging` to make it easier to use. That's why all types and keys are stored inside `lib/messaging` directory:
```ts title="lib/messaging/index.ts"
import { defineExtensionMessaging } from "@webext-core/messaging";
export const Message = {
HELLO: "hello",
} as const;
export type Message = (typeof Message)[keyof typeof Message];
interface Messages {
[Message.HELLO]: (message: string) => string;
}
export const { onMessage, sendMessage } = defineExtensionMessaging<Messages>();
```
There you need to define what will be handled under each key. To make it more secure, only `Message` enum and `onMessage` and `sendMessage` functions are exported from the module.
All message handlers are located in `src/app/background/messaging` directory under respective subdirectories.
To create a message handler, create a TypeScript module in the `background/messaging` directory. Then, include your handlers for all keys related to the message:
```ts title="app/background/messaging/hello.ts"
import { onMessage, Message } from "~/lib/messaging";
onMessage(Message.HELLO, (req) => {
const result = await querySomeApi(req.body.id);
return result;
});
```
<Callout title="Don't forget to import!" type="warn">
To make your handlers available across your extension, you need to import them
in the `background/index.ts` file. That way they could be interpreted by the
build process facilitated by WXT.
</Callout>
## Sending messages
Extension pages, content scripts, or tab pages can send messages to the handlers using the `sendMessage` function. Since we orchestrate your handlers behind the scenes, the message names are typed and will enable autocompletion in your editor:
```tsx title="app/popup/index.tsx"
import { sendMessage, Message } from "~/lib/messaging";
...
const response = await sendMessage(Message.HELLO, "Hello, world!");
console.log(response);
...
```
As it's an asynchronous operation, it's advisable to use [@tanstack/react-query](https://tanstack.com/query/latest/docs/framework/react/overview) integration to handle the response on the client side.
We're already doing it that way when fetching auth session in the `User` component:
```tsx title="hello.tsx"
export const Hello = () => {
const { data, isLoading } = useQuery({
queryKey: [Message.HELLO],
queryFn: () => sendMessage(Message.HELLO, "Hello, world!"),
});
if (isLoading) {
return <p>Loading...</p>;
}
/* do something with the data... */
return <p>{data?.message}</p>;
};
```
<Cards>
<Card href="https://webext-core.aklinker1.io/messaging/installation/" title="Messaging API" description="webext-core.aklinker1.io" />
<Card title="Message passing" description="developer.chrome.com" href="https://developer.chrome.com/docs/extensions/develop/concepts/messaging" />
</Cards>

View File

@@ -0,0 +1,50 @@
---
title: Overview
description: Learn about the structure of the extension app.
url: /docs/extension/structure/overview
---
# Overview
Every browser extension is different and can include different parts, removing the ones that are not needed.
TurboStarter ships with all the things you need to start developing your own extension including:
* **Popup window** - a small window that appears when the user clicks the extension icon.
* **Options page** - a page that appears when user enters extension settings.
* **Side panel** - a panel that appears when the user clicks sidepanel.
* **New tab page** - a page that appears when the user opens a new tab.
* **Devtools page** - a page that appears when the user opens the browser's devtools.
* **Tab pages** - custom pages shipped with the extension.
* **Content scripts** - injected scripts that run in the browser page.
* **Background scripts** - scripts that run in the background.
* **Message passing** - a way to communicate between different parts of the extension.
* **Storage** - a way to store data in the extension.
All the entrypoints are defined in `apps/extension/src/app` directory (it's similar to file-based routing in Next.js and Expo).
This directory acts as a source for WXT framework which is used to build the extension. It has the following structure:
<Files>
<Folder name="app" defaultOpen>
<Folder name="background - Background service worker" />
<Folder name="content - Content scripts" />
<Folder name="devtools - Devtools page with custom panels" />
<Folder name="newtab - New tab page" />
<Folder name="options - Options page" />
<Folder name="popup - Popup window" />
<Folder name="sidepanel - Side panel" />
<Folder name="tabs - Custom pages shipped with the extension" />
</Folder>
</Files>
By structurizing it this way, we can easily add new entrypoints in the future and extend rest of the extension independently from each other.
We'll go through each part and explain the purpose of it, check following sections for more details:

View File

@@ -0,0 +1,77 @@
---
title: Pages
description: Get started with your extension's pages.
url: /docs/extension/structure/pages
---
# Pages
Extension pages are built-in pages recognized by the browser. They include the extension's popup, options, sidepanel and newtab pages.
<Callout>
As WXT is based on Vite, it has very powerful [HMR support](https://vite.dev/guide/features#hot-module-replacement). This means that you don't need to refresh the extension manually when you make changes to the code.
</Callout>
## Popup
The popup page is a small dialog window that opens when a user clicks on the extension's icon in the browser toolbar. It is the most common type of extension page.
![Popup window](/images/docs/extension/structure/popup.png)
<Cards>
<Card title="Add a popup" href="https://developer.chrome.com/docs/extensions/develop/ui/add-popup" description="developer.chrome.com" />
<Card title="Entrypoints" href="https://wxt.dev/guide/essentials/entrypoints.html" description="wxt.dev" />
</Cards>
## Options
The options page is meant to be a dedicated place for the extension's settings and configuration.
![Options page](/images/docs/extension/structure/options.png)
<Card title="Give users options" href="https://developer.chrome.com/docs/extensions/develop/ui/options-page" description="developer.chrome.com" />
## Devtools
The devtools page is a custom page (including panels) that opens when a user opens the extension's devtools panel.
![Devtools page](/images/docs/extension/structure/devtools.png)
<Card title="Extend devtools" href="https://developer.chrome.com/docs/extensions/how-to/devtools/extend-devtools" description="developer.chrome.com" />
## New tab
The new tab page is a custom page that opens when a user opens a new tab in the browser.
![New tab page](/images/docs/extension/structure/newtab.png)
<Card title="Override Chrome pages" href="https://developer.chrome.com/docs/extensions/develop/ui/override-chrome-pages" description="developer.chrome.com" />
## Side panel
The side panel is a custom page that opens when a user clicks on the extension's icon in the browser toolbar.
![Side panel](/images/docs/extension/structure/sidepanel.png)
<Card title="Side panel" href="https://developer.chrome.com/docs/extensions/reference/api/sidePanel" description="developer.chrome.com" />
## Tabs
Unlike traditional extension pages, tab (unlisted) pages are just regular web pages shipped with your extension bundle. Extensions generally redirect to or open these pages programmatically, but you can link to them as well.
They could be useful for following cases:
* when you want to show a some page when user first installs your extension
* when you want to have dedicated pages for authentication
* when you need more advanced routing setup
![Tab page](/images/docs/extension/structure/tabs.png)
Your tab page will be available under the `/tabs` path in the extension bundle. It will be accessible from the browser under the URL:
```
chrome-extension://<your-extension-id>/tabs/your-tab-page.html
```
<Card title="Unlisted pages" href="https://wxt.dev/guide/essentials/entrypoints.html#unlisted-pages" description="wxt.dev" />

View File

@@ -0,0 +1,109 @@
---
title: Storage
description: Learn how to store data in your extension.
url: /docs/extension/structure/storage
---
# Storage
TurboStarter leverages `wxt/storage` library to handle persistent storage for your extension. It's a utility library from that abstracts the persistent storage API available to browser extensions.
It falls back to localStorage when the extension storage API is unavailable, allowing for state sync between extension pages, content scripts, background service workers and web pages.
<Callout>
To use the `wxt/storage` API, the "storage" permission **must** be added to the manifest:
```ts title="wxt.config.ts"
export default defineConfig({
manifest: {
permissions: ["storage"],
},
});
```
</Callout>
## Storing data
The base Storage API is designed to be easy to use. It is usable in every extension runtime such as background service workers, content scripts and extension pages.
TurboStarter ships with predefined storage used to handle [theming](/docs/extension/customization/styling) in your extension, but you can create your own storage as well.
All storage-related methods and types are located in `lib/storage` directory.
```ts title="lib/storage/index.ts"
export const StorageKey = {
THEME: "local:theme",
} as const;
export type StorageKey = (typeof StorageKey)[keyof typeof StorageKey];
```
Then, to make it available around your extension, we're setting it up and providing default values:
```ts title="lib/storage/index.ts"
import { storage as browserStorage } from "wxt/storage";
import { appConfig } from "~/config/app";
import type { ThemeConfig } from "@turbostarter/ui";
const storage = {
[StorageKey.THEME]: browserStorage.defineItem<ThemeConfig>(StorageKey.THEME, {
fallback: appConfig.theme,
}),
} as const;
```
To learn more about customizing your storage, syncing state or setup automatic backups please refer to the [official documentation](https://wxt.dev/storage.html).
## Consuming storage
To consume storage in your extension, you can use the `useStorage` React hook that is automatically provided to every part of the extension. The hook API is designed to streamline the state-syncing workflow between the different pieces of an extension.
Here is an example on how to consume our theme storage in `Layout` component:
```tsx title="modules/common/layout/layout.tsx"
import { StorageKey, useStorage } from "~/lib/storage";
export const Layout = ({ children }: { children: React.ReactNode }) => {
const { data } = useStorage(StorageKey.THEME);
return (
<div id="root" data-theme={data.color}>
{children}
</div>
);
};
```
Congrats! You've just learned how to persist and consume global data in your extension 🎉
For more advanced use cases, please refer to the [official documentation](https://wxt.dev/storage.html).
### Usage with Firefox
To use the storage API on Firefox during development you need to add an addon ID to your manifest, otherwise, you will get this error:
> Error: The storage API will not work with a temporary addon ID. Please add an explicit addon ID to your manifest. For more information see [https://mzl.la/3lPk1aE](https://mzl.la/3lPk1aE)
To add an addon ID to your manifest, add this to your package.json:
```ts title="wxt.config.ts"
export default defineConfig({
manifest: {
browser_specific_settings: {
gecko: {
id: "your-id@example.com",
},
},
},
});
```
During development, you may use any ID. If you have published your extension, you need to use the ID assigned by [Firefox Add-ons](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons).
<Cards>
<Card title="Storage API" href="https://wxt.dev/storage.html" description="wxt.dev" />
<Card title="chrome.storage" href="https://developer.chrome.com/docs/extensions/reference/api/storage" description="developer.chrome.com" />
</Cards>

View File

@@ -0,0 +1,15 @@
---
title: E2E tests
description: Simulate real user scenarios across the entire stack with automated end-to-end test tools and examples.
url: /docs/extension/tests/e2e
---
# E2E tests
<Callout title="E2E testing is coming soon">
End-to-end (E2E) tests will be available soon, allowing you to automate testing of real user flows and interactions across your application.
Stay tuned for updates as we roll out robust E2E testing resources and examples.
[See roadmap](https://github.com/orgs/turbostarter/projects/1)
</Callout>

View File

@@ -0,0 +1,136 @@
---
title: Unit tests
description: Write and run fast unit tests for individual functions and components with instant feedback.
url: /docs/extension/tests/unit
---
# Unit tests
Unit tests are a type of automated test where individual units or components are tested. The "unit" in "unit test" refers to the smallest testable parts of an application. These tests are designed to verify that each unit of code performs as expected.
TurboStarter uses [Vitest](https://vitest.dev) as the unit testing framework. It's a blazing-fast test runner built on top of [Vite](https://vitejs.dev), designed for modern JavaScript and TypeScript projects.
<Callout title="Why Vitest?">
If you've used [Jest](https://jestjs.io) before, you already know Vitest - it shares the same API. But Vitest is built for speed: native TypeScript support without transpilation, parallel test execution, and a smart watch mode that only re-runs tests affected by your changes.
It comes with everything you need out of the box - code coverage, snapshot testing, mocking, and a slick UI for debugging. Fast feedback, zero configuration.
</Callout>
## Why write unit tests?
Unit tests give you **fast, focused feedback** on small pieces of your code - individual functions, hooks, or components. Instead of debugging an entire page or flow, you can verify just the logic you care about in isolation.
They also act as **living documentation**: a good test tells you how a function is supposed to behave, which edge cases are important, and what assumptions the code makes. This makes it much easier to safely refactor or extend features later.
In TurboStarter, unit tests are designed to be **cheap and quick to run**, so you can keep Vitest running in watch mode while you code. Every change you make is immediately checked, helping you catch regressions before they ever reach integration or endtoend tests.
## Configuration
TurboStarter configures Vitest to be **as simple as possible**, while still taking advantage of [Turborepo's caching](https://turborepo.com/docs/crafting-your-repository/caching) and Vitest's [Test Projects](https://vitest.dev/guide/projects).
```ts title="vitest.config.ts"
import { mergeConfig } from "vitest/config";
import baseConfig from "@turbostarter/vitest-config/base";
export default mergeConfig(baseConfig, {
test: {
/* your extended test configuration here */
},
});
```
* **Per-package tests**: each package that has unit tests defines its own `test` script. This keeps the configuration close to the code and makes it easy to add tests to any workspace.
* **Turbo tasks for CI**: the root `test` task (`pnpm test`) uses `turbo run test` to execute all package-level test scripts with smart caching, which is ideal for CI pipelines where you want to avoid re-running unchanged tests.
* **Vitest Test Projects for local dev**: a root Vitest configuration uses [Test Projects](https://vitest.dev/guide/projects) to run all unit test suites from a single command, which is perfect for local development when you want fast feedback across the whole monorepo.
This **hybrid setup** combines Turborepo and Vitest Projects in a way that fits TurboStarter's principles: cached, package-aware runs in CI, and a single, unified Vitest entry point for local development.
You can read more about this setup in the official documentation guides listed below.
<Cards>
<Card title="Vitest" description="turborepo.com" href="https://turborepo.com/docs/guides/tools/vitest" />
<Card title="Test Projects" description="vitest.dev" href="https://vitest.dev/guide/projects" />
</Cards>
## Running tests
There are a few different ways to run unit tests, depending on what you're doing:
* **CI / full test run** - at the root of the repo:
```bash
pnpm test
```
This runs `turbo run test`, which executes all `test` scripts in packages that define them, with Turborepo handling caching so unchanged packages are skipped. This is what you should use in your CI/CD pipeline.
* **One-off local run with Vitest Projects**:
```bash
pnpm test:projects
```
This uses Vitest [Test Projects](https://vitest.dev/guide/projects) to run all configured unit test suites from a single command, which is great when you want to quickly validate the whole monorepo locally.
* **Watch mode during development**:
```bash
pnpm test:projects:watch
```
This starts Vitest in watch mode across all Test Projects. As you edit files, only the affected tests are re-run, giving you fast feedback while you work.
## Code coverage
Unit test coverage helps you understand **how much** of your code is being tested. While it can't guarantee bug-free code, it shines a light on untested paths that could hide issues or regressions.
To generate a code coverage report for all unit tests, run:
```bash
pnpm turbo test:coverage
```
This command runs the coverage task across all relevant packages (using Turborepo) and collects the results into a single coverage output.
To open the coverage report in your browser:
```bash
pnpm turbo test:coverage:view
```
This will build the HTML report and launch it using your default browser, so you can explore which files and branches are covered.
<Callout title="Uploading coverage as an artifact">
You can also store the generated coverage report as a [GitHub Actions artifact](https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts) during your CI/CD pipeline, just add the following steps to your workflow job:
```yaml title=".github/workflows/ci.yml"
# your workflow job configuration here
- name: 📊 Generate coverage
run: pnpm turbo test:coverage
- name: 🗃️ Archive coverage report
uses: actions/upload-artifact@v5
with:
name: coverage-${{ github.sha }}
path: tooling/vitest/coverage/report
```
This will generate a test coverage report and upload it as an artifact, so you can access it from GitHub Actions tab for later inspection.
</Callout>
A high coverage percentage means your tests execute most lines and branches - but the quality and relevance of your tests matter more than the raw number. Use coverage reports to spot gaps and guide improvements, not as the sole metric of test health.
![Code coverage](/images/docs/code-coverage.png)
## Best practices
Unit tests should work **for you**, not the other way around. Focus on writing tests that make it easier to change code with confidence, not on satisfying arbitrary rules or reaching a magic number in a dashboard.
Code coverage is a **useful metric**, but it **SHOULD NOT** be the goal. It's better to have a smaller set of highvalue tests that cover critical paths and edge cases than a huge suite of fragile tests that are hard to maintain.
When in doubt, ask: *“Does this test give **me** confidence that I can change this code without breaking users?”* If the answer is no, refactor or remove it.
Finally, keep unit tests focused on **small, isolated pieces of logic**. More advanced flows — like multi-step user journeys, cross-service interactions, or full-page behavior — are better covered by [end-to-end (E2E) tests](/docs/web/tests/e2e), where you can verify the system as a whole.

View File

@@ -0,0 +1,79 @@
---
title: Installation
description: Find answers to common extension installation issues.
url: /docs/extension/troubleshooting/installation
---
# Installation
## Cannot clone the repository
Issues related to cloning the repository are usually related to a Git misconfiguration in your local machine. The commands displayed in this guide using SSH: these will work only if you have setup your SSH keys in Github.
If you run into issues, [please make sure you follow this guide to set up your SSH key in Github.](https://docs.github.com/en/authentication/connecting-to-github-with-ssh)
If this also fails, please use HTTPS instead. You will be able to see the commands in the repository's Github page under the "Clone" dropdown.
Please also make sure that the account that accepted the invite to TurboStarter, and the locally connected account are the same.
## Local database doesn't start
If you cannot run the local database container, it's likely you have not started [Docker](https://docs.docker.com/get-docker/) locally. Our local database requires Docker to be installed and running.
Please make sure you have installed Docker (or compatible software such as [Colima](https://github.com/abiosoft/colima), [Orbstack](https://github.com/orbstack/orbstack)) and that is running on your local machine.
Also, make sure that you have enough [memory and CPU allocated](https://docs.docker.com/engine/containers/resource_constraints/) to your Docker instance.
## Permissions issues
If some feature of your extension is not working, it's possible that you're missing a permission in the manifest config.
Make sure to check the [permissions](/docs/extension/configuration/manifest#overriding-manifest) section in the manifest config file.
## I don't see my translations
If you don't see your translations appearing in the application, there are a few common causes:
1. Check that your translation `.json` files are properly formatted and located in the correct directory
2. Verify that the language codes in your configuration match your translation files
3. Enable debug mode (`debug: true`) in your i18next configuration to see detailed logs
[Read more about configuration for translations](/docs/extension/internationalization#configuration)
## "Module not found" error
This issue is mostly related to either dependency installed in the wrong package or issues with the file system.
The most common cause is incorrect dependency installation. Here's how to fix it:
1. Clean the workspace:
```bash
pnpm clean
```
2. Reinstall the dependencies:
```bash
pnpm i
```
If you're adding new dependencies, make sure to install them in the correct package:
```bash
# For main app dependencies
pnpm install --filter mobile my-package
# For a specific package
pnpm install --filter @turbostarter/ui my-package
```
If the issue persists, please check the file system for any issues.
### Windows OneDrive
OneDrive can cause file system issues with Node.js projects due to its file syncing behavior. If you're using Windows with OneDrive, you have two options to resolve this:
1. Move your project to a location outside of OneDrive-synced folders (recommended)
2. Disable OneDrive sync specifically for your development folder
This prevents file watching and symlink issues that can occur when OneDrive tries to sync Node.js project files.

View File

@@ -0,0 +1,62 @@
---
title: Publishing
description: Find answers to common publishing issues.
url: /docs/extension/troubleshooting/publishing
---
# Publishing
## My extension submission was rejected
If your extension submission was rejected, you probably got an email with the reason. You'll need to fix the issues and upload a new build of your extension to the store and send it for review again.
Make sure to follow the [guidelines](/docs/extension/marketing) when submitting your extension to ensure that everything is setup correctly.
## Version number mismatch
If you get version number conflicts when submitting:
1. Ensure your `manifest.json` version matches what's in the store
2. Increment the version number appropriately for each new submission
3. Make sure the version follows semantic versioning (e.g., `1.0.1`)
## Missing permissions in manifest
If your extension is rejected due to permission issues:
1. Review the permissions declared in your `manifest.json`
2. Ensure all permissions are properly justified in your submission
3. Remove any unused permissions that aren't essential
4. Consider using optional permissions where possible
[Learn more about permissions](/docs/extension/configuration/manifest#permissions)
## Content Security Policy (CSP) violations
If your extension is rejected due to CSP issues:
1. Check your manifest's `content_security_policy` field
2. Ensure all external resources are properly whitelisted
3. Remove any unsafe inline scripts or eval usage
4. Use more secure alternatives like `browser.scripting.executeScript`
## My extension crashes on production build
If the extension works during development but crashes after publishing or when loaded unpacked in production mode, check these common causes:
1. **Uncaught runtime errors** in the background service worker or content scripts. Open `chrome://extensions` (or `about:debugging` in Firefox) → enable Developer mode → Inspect the service worker/content script and check the console for stack traces.
2. **Missing permissions or host permissions** causing APIs to throw (e.g., network calls, tabs access). Ensure required `permissions` and `host_permissions` are declared in `manifest.json`.
3. **CSP blocking resources** (inline scripts/styles, remote fonts, or endpoints). Verify `content_security_policy` and update code to avoid unsafe patterns.
4. **Missing assets or incorrect paths** referenced in `manifest.json` (`icons`, `web_accessible_resources`, `action.default_popup`, etc.). Confirm files exist in the final build output and paths match.
5. **Build-time variables not resolved**. If you rely on environment variables, ensure theyre inlined at build time or have safe fallbacks at runtime. Example:
```js
const apiUrl = env.VITE_SITE_URL ?? "https://api.example.com";
```
6. **Module format or bundler config issues** (MV3 service worker must be ESM if `type: 'module'`). Align bundler output with your manifest expectations and rebuild.
Try this:
1. Reproduce with a production bundle locally and load it as an unpacked extension; inspect background and content script logs for errors.
2. Validate `manifest.json` and ensure all referenced files are present in the build output.
3. Temporarily relax CSP locally to confirm whether CSP is the cause; then apply a compliant fix (dont ship relaxed CSP).
4. Add fallbacks for any build-time variables and rebuild.

View File

@@ -0,0 +1,59 @@
---
title: AI
description: Learn how to use AI integration in your mobile app.
url: /docs/mobile/ai
---
# AI
As AI integration for [web](/docs/web/ai/overview), [extension](/docs/extension/ai), and mobile is based on the same battle-tested [Vercel AI SDK](https://sdk.vercel.ai/docs/introduction), the implementation is very similar across platforms.
In this section, we'll focus on how to consume AI responses in the mobile app. For server-side implementation details, please refer to the [web documentation](/docs/web/ai/overview).
## Features
The most common AI integration features are also supported in the mobile app:
* **Chat**: Build chat interfaces inside native mobile apps.
* **Streaming**: Receive AI responses as soon as the model starts generating them, without waiting for the full response to be completed.
* **Image generation**: Generate images based on a given prompt.
You can easily compose your application using these building blocks or extend them to suit your specific needs.
## Usage
The usage of AI integration in the mobile app is the same as for [web app](/docs/web/ai/configuration#client-side) and [browser extension](/docs/extension/ai#server--client). We use the exact same [API endpoint](/docs/web/ai/configuration#api-endpoint), and since TurboStarter ships with built-in support for streaming on mobile, we can leverage it to display answers incrementally to the user as they're generated.
```tsx title="ai.tsx"
import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
const AI = () => {
const { messages } = useChat({
transport: new DefaultChatTransport({
api: "/api/ai/chat",
}),
});
return (
<View>
{messages.map((message) => (
<Text key={message.id}>
{message.parts.map((part, i) => {
switch (part.type) {
case "text":
return <Text key={`${message.id}-${i}`}>{part.text}</Text>;
}
})}
</Text>
))}
</View>
);
};
export default AI;
```
By leveraging this integration, we can easily manage the state of the AI request and update the UI as soon as the response is ready.
TurboStarter ships with a ready-to-use implementation of AI chat, allowing you to see this solution in action. Feel free to reuse or modify it according to your needs.

View File

@@ -0,0 +1,161 @@
---
title: Configuration
description: Learn how to configure mobile analytics in TurboStarter.
url: /docs/mobile/analytics/configuration
---
# Configuration
The `@turbostarter/analytics-mobile` package offers a streamlined and flexible approach to tracking events in your TurboStarter mobile app using various analytics providers. It abstracts the complexities of different analytics services and provides a consistent interface for event tracking.
In this section, we'll guide you through the configuration process for each supported provider.
Note that the configuration is validated against a schema, so you'll see error messages in the console if anything is misconfigured.
## Permissions
First and foremost, to start tracking any metrics from your app (and to do so legally), you need to ask your users for permission. It's [required](https://support.apple.com/en-us/102420), and you're not allowed to collect any data without it.
To make this process as simple as possible, TurboStarter comes with a `useTrackingPermissions` hook that you can use to access the user's consent status. It will handle asking for permission automatically as well as process updates made through the general phone settings.
```tsx
import { useTrackingPermissions } from "@turbostarter/analytics-mobile";
export const MyComponent = () => {
const granted = useTrackingPermissions();
if (granted) {
// Start tracking
} else {
// Disable tracking
}
};
```
Also, for Apple, you must declare the tracking justification via [App Tracking Transparency](https://developer.apple.com/documentation/apptrackingtransparency). It comes pre-configured in TurboStarter via the [Expo Config Plugin](https://docs.expo.dev/versions/latest/config/app/#plugins), where you can provide a custom message to the user:
```ts title="app.config.ts"
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
plugins: [
[
"expo-tracking-transparency",
{
/* 🍎 Describe why you need access to the user's data */
userTrackingPermission:
"This identifier will be used to deliver personalized ads to you.",
},
],
],
});
```
This way, we ensure that the user is aware of the data we collect and can make an informed decision. If you don't provide this information, your app is likely to be rejected by Apple and/or Google during the [review process](/docs/mobile/publishing/checklist#send-to-review).
## Providers
TurboStarter supports multiple analytics providers, each with its own unique configuration. Below, you'll find detailed information on how to set up and use each supported provider. Choose the one that best suits your needs and follow the instructions in the respective accordion section.
<Accordions>
<Accordion title="Google Analytics" id="google-analytics">
To use Google Analytics as your analytics provider, you need to [configure and link a Firebase project to your app](/docs/mobile/installation/firebase).
After that, you can proceed with the installation of the analytics package:
```bash
pnpm add --filter @turbostarter/analytics-mobile @react-native-firebase/analytics
```
Also, make sure to activate the Google Analytics provider as your analytics provider by updating the exports in:
```ts title="index.ts"
// [!code word:google-analytics]
export * from "./google-analytics";
export * from "./google-analytics/env";
```
To customize the provider, you can find its definition in `packages/analytics/mobile/src/providers/google-analytics` directory.
For more information, please refer to the [React Native Firebase documentation](https://rnfirebase.io/analytics/usage).
![Google Analytics dashboard](/images/docs/web/analytics/google/dashboard.jpg)
</Accordion>
<Accordion title="PostHog" id="posthog">
<Callout type="info" title="You can also use it for monitoring!">
PostHog is also one of pre-configured providers for [monitoring](/docs/mobile/monitoring/overview) in TurboStarter mobile apps. You can learn more about it [here](/docs/mobile/monitoring/posthog).
</Callout>
To use PostHog as your analytics provider, you need to configure a PostHog instance. You can obtain the [Cloud](https://app.posthog.com/signup) instance by [creating an account](https://app.posthog.com/signup) or [self-host](https://posthog.com/docs/self-host) it.
Then, create a project and, based on your [project settings](https://app.posthog.com/project/settings), fill the following environment variables in your `.env.local` file in `apps/mobile` directory and your `eas.json` file:
```dotenv
EXPO_PUBLIC_POSTHOG_KEY="your-posthog-api-key"
EXPO_PUBLIC_POSTHOG_HOST="your-posthog-instance-host"
```
Also, make sure to activate the PostHog provider as your analytics provider by updating the exports in:
```ts title="index.ts"
// [!code word:posthog]
export * from "./posthog";
export * from "./posthog/env";
```
To customize the provider, you can find its definition in `packages/analytics/mobile/src/providers/posthog` directory.
For more information, please refer to the [PostHog documentation](https://posthog.com/docs).
![PostHog dashboard](/images/docs/web/analytics/posthog.png)
</Accordion>
<Accordion title="Mixpanel" id="mixpanel">
To use Mixpanel as your analytics provider, you need to [create an account](https://mixpanel.com/) and [obtain your project token](https://help.mixpanel.com/hc/en-us/articles/115004502806-Find-Project-Token).
Then, set it as an environment variable in your `.env.local` file in the `apps/mobile` directory and your `eas.json` file:
```dotenv
EXPO_PUBLIC_MIXPANEL_TOKEN="your-project-token"
```
Also, make sure to activate the Mixpanel provider as your analytics provider by updating the exports in:
```ts title="index.ts"
// [!code word:mixpanel]
export * from "./mixpanel";
export * from "./mixpanel/env";
```
To customize the provider, you can find its definition in `packages/analytics/mobile/src/providers/mixpanel` directory.
For more information, please refer to the [Mixpanel documentation](https://docs.mixpanel.com/).
</Accordion>
</Accordions>
## Context
To enable tracking events, capturing screen views and other analytics features, you need to wrap your app with the `Provider` component that's implemented by every provider and available through the `@turbostarter/analytics-mobile` package:
```tsx title="providers.tsx"
// [!code word:AnalyticsProvider]
import { memo } from "react";
import { Provider as AnalyticsProvider } from "@turbostarter/analytics-mobile";
interface ProvidersProps {
readonly children: React.ReactNode;
}
export const Providers = memo<ProvidersProps>(({ children }) => {
return (
<OtherProviders>
<AnalyticsProvider>{children}</AnalyticsProvider>
</OtherProviders>
);
});
Providers.displayName = "Providers";
```
By implementing this setup, you ensure that all analytics events are properly tracked from your mobile app code. This configuration allows you to safely utilize the [Analytics API](/docs/mobile/analytics/tracking) within your components, enabling comprehensive event tracking and data collection.

View File

@@ -0,0 +1,50 @@
---
title: Overview
description: Get started with mobile analytics in TurboStarter.
url: /docs/mobile/analytics/overview
---
# Overview
When it comes to mobile app analytics, we can distinguish between two types:
* **Store listing analytics**: Used to track the performance of your mobile app's store listing (e.g., how many people have viewed your app in the store or how many have installed it).
* **In-app analytics**: Tracks user actions within your mobile app (e.g., how many users entered a specific screen, how many users clicked on a specific button, etc.).
The `@turbostarter/analytics-mobile` package provides a set of tools to easily implement both types of analytics in your mobile app.
## Store listing analytics
Interpreting your mobile app's store listing metrics can help you evaluate how changes to your app and store listing affect conversion rates. For example, you can identify keywords that users are searching for to optimize your app's store listing.
While each store implements a different set of metrics, there are some common ones you should be aware of:
* **Downloads**: The total number of times your app was downloaded, including both first-time downloads and re-downloads.
* **Sales**: The total number of pre-orders, first-time app downloads, in-app purchases, and their associated sales.
* **Usage**: A variety of user engagement metrics, such as installations, sessions, crashes, and active devices.
To learn more about these or other metrics (e.g., how to create custom reports or KPIs), please refer to the official documentation of the store you're publishing to:
<Cards>
<Card title="Overview of reporting tools" description="developer.apple.com" href="https://developer.apple.com/help/app-store-connect/measure-app-performance/overview-of-reporting-tools" />
<Card title="View app statistics" description="support.google.com" href="https://support.google.com/googleplay/android-developer/answer/139628?hl=en&co=GENIE.Platform%3DDesktop&oco=1" />
</Cards>
## In-app analytics
TurboStarter comes with built-in analytics support for multiple providers as well as a unified API for tracking events. This API enables you to easily and consistently track user behavior and app usage across your mobile application.
To learn more about each provider and how to configure them, see their respective sections:
<Cards>
<Card title="Google Analytics" href="/docs/mobile/analytics/configuration#google-analytics" />
<Card title="PostHog" href="/docs/mobile/analytics/configuration#posthog" />
<Card title="Mixpanel" href="/docs/mobile/analytics/configuration#mixpanel" />
</Cards>
All configuration and setup is built-in with a unified API, allowing you to switch between providers by simply changing the exports. You can even introduce your own provider without breaking any tracking-related logic.
In the following sections, we'll cover how to set up each provider and how to track events in your application.

View File

@@ -0,0 +1,84 @@
---
title: Tracking events
description: Learn how to track events in your TurboStarter mobile app.
url: /docs/mobile/analytics/tracking
---
# Tracking events
The strategy for tracking events that every provider has to implement is extremely simple:
```ts
export type AllowedPropertyValues = string | number | boolean;
type TrackFunction = (
event: string,
data?: Record<string, AllowedPropertyValues>,
) => void;
export interface AnalyticsProviderStrategy {
Provider: ({ children }: { children: React.ReactNode }) => React.ReactNode;
track: TrackFunction;
}
```
<Callout>
You don't need to worry much about this implementation, as all the providers are already configured for you. However, it's useful to be aware of this structure if you plan to add your own custom provider.
</Callout>
As shown above, each provider must supply two key elements:
1. `Provider` - a component that [wraps your app](/docs/mobile/analytics/configuration#context).
2. `track` - a function responsible for sending event data to the provider.
To track an event, you simply need to invoke the `track` method, passing the event name and an optional data object:
```tsx
import { track } from "@turbostarter/analytics-mobile";
export const MyComponent = () => {
return (
<Pressable onPress={() => track("button.click", { country: "US" })}>
Track event
</Pressable>
);
};
```
In most mobile apps, you'll only ever need to use the `track` method to track events. You can use it anywhere in your app code—such as in response to user interactions, navigation events, or custom actions - by simply calling `track` with an event name and optional properties.
## Identifying users
Linking events to specific users enables you to build a full picture of how they're using your product across different sessions, devices, and platforms.
For identification purposes, we're extending the strategy with the `identify` and `reset` methods. They are optional and only needed if you want to identify users in your app and associate their actions with a specific user ID.
```ts
type IdentifyFunction = (
userId: string,
traits?: Record<string, AllowedPropertyValues>,
) => void;
export interface AnalyticsProviderClientStrategy {
identify: IdentifyFunction;
reset: () => void;
}
```
To identify users, call the `identify` method, passing the user's ID and an optional traits object:
```tsx
import { identify } from "@turbostarter/analytics-mobile";
identify("user-123", { name: "John Doe" });
```
This will associate all future events with the user's ID, allowing you to track user behavior and gain valuable insights into your application's usage patterns.
<Callout title="Configured by default!">
The `identify` method is configured out-of-the-box to react on changes to the user's authentication state.
When the user is authenticated, the `identify` method will be called with the user's ID and the user's traits. When the user is logged out, the `reset` method will be called to clear the existing user identification.
</Callout>
Congratulations! You've now mastered event tracking in your TurboStarter mobile app. With this knowledge, you're well-equipped to analyze user behaviors and gain valuable insights into your application's usage patterns. Happy analyzing!

View File

@@ -0,0 +1,213 @@
---
title: Using API client
description: How to use API client to interact with the API.
url: /docs/mobile/api/client
---
# Using API client
In mobile app code, you can only access the API client from the **client-side.**
When you create a new component or screen and want to fetch some data, you can use the API client to do so.
## Creating a client
We're creating a client-side API client in `apps/mobile/src/lib/api/index.tsx` file. It's a simple wrapper around the [@tanstack/react-query](https://tanstack.com/query/latest/docs/framework/react/overview) that fetches or mutates data from the API.
It also requires wrapping your app in a `QueryClientProvider` component to provide the API client to the rest of the app:
```tsx title="_layout.tsx"
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<QueryClientProvider>
<SafeAreaProvider>
<Stack>
...
<Stack.Screen name="index" />
...
</Stack>
<StatusBar barStyle="light-content" />
</SafeAreaProvider>
</QueryClientProvider>
);
}
```
<Callout type="warn" title="Ensure correct API url">
Inside the `apps/mobile/src/lib/api/utils.ts` file we're calling a function to get base url of your api, so make sure it's set correctly (especially on production) and your web api endpoint is corresponding with the name there.
```tsx title="utils.ts"
const getBaseUrl = () => {
/**
* Gets the IP address of your host-machine. If it cannot automatically find it,
* you'll have to manually set it. NOTE: Port 3000 should work for most but confirm
* you don't have anything else running on it, or you'd have to change it.
*
* **NOTE**: This is only for development. In production, you'll want to set the
* baseUrl to your production API URL.
*/
const debuggerHost = Constants.expoConfig?.hostUri;
const localhost = debuggerHost?.split(":")[0];
if (!localhost) {
console.warn("Failed to get localhost. Pointing to production server...");
return env.EXPO_PUBLIC_SITE_URL;
}
return `http://${localhost}:3000`;
};
```
As you can see we're relying on your machine IP address for local development (in case you want to open the app from another device) or on the [environment variables](/docs/mobile/configuration/environment-variables) in production to get it, so there shouldn't be any issues with it, but in case, please be aware where to find it 😉
</Callout>
## Queries
Of course, everything comes already configured for you, so you just need to start using `api` in your components/screens.
For example, to fetch the list of posts you can use the `useQuery` hook:
```tsx title="app/(tabs)/tab-one.tsx"
import { api } from "~/lib/api";
export default function TabOneScreen() {
const { data: posts, isLoading } = useQuery({
queryKey: ["posts"],
queryFn: async () => {
const response = await api.posts.$get();
if (!response.ok) {
throw new Error("Failed to fetch posts!");
}
return response.json();
},
});
if (isLoading) {
return <Text>Loading...</Text>;
}
/* do something with the data... */
return (
<View>
<Text>{JSON.stringify(posts)}</Text>
</View>
);
}
```
It's using the `@tanstack/react-query` [useQuery API](https://tanstack.com/query/latest/docs/framework/react/reference/useQuery), so you shouldn't have any troubles with it.
<Cards>
<Card title="Hono RPC" description="hono.dev" href="https://hono.dev/docs/guides/rpc" />
<Card title="useQuery hook | Tanstack Query" description="tanstack.com" href="https://tanstack.com/query/latest/docs/framework/react/reference/useQuery" />
</Cards>
## Mutations
If you want to perform a mutation in your mobile code, you can use the `useMutation` hook that comes straight from the integration with [Tanstack Query](https://tanstack.com/query):
```tsx title="form.tsx"
import { api } from "~/lib/api";
export function CreatePost() {
const queryClient = useQueryClient();
const { mutate } = useMutation({
mutationFn: async (post: PostInput) => {
const response = await api.posts.$post(post);
if (!response.ok) {
throw new Error("Failed to create post!");
},
},
onSuccess: () => {
toast.success("Post created successfully!");
queryClient.invalidateQueries({ queryKey: ["posts"] });
},
});
return (
<Form>
<Button onPress={onSubmit(mutate)}>Submit</Button>
</Form>
);
}
```
Here, we're also invalidating the query after the mutation is successful. This is a very important step to make sure that the data is updated in the UI.
<Cards>
<Card title="useMutation hook" description="tanstack.com" href="https://tanstack.com/query/latest/docs/framework/react/reference/useMutation" />
<Card title="Query invalidation" description="tanstack.com" href="https://tanstack.com/query/latest/docs/framework/react/guides/query-invalidation" />
</Cards>
## Handling responses
As you can see in the examples above, the [Hono RPC](https://hono.dev/docs/guides/rpc) client returns a plain `Response` object, which you can use to get the data or handle errors. However, implementing this handling in every query or mutation can be tedious and will introduce unnecessary boilerplate in your codebase.
That's why we've developed the `handle` function that unwraps the response for you, handles errors, and returns the data in a consistent format. You can safely use it with any procedure from the API client:
<Tabs items={["Queries", "Mutations"]}>
<Tab value="Queries">
```tsx
// [!code word:handle]
import { handle } from "@turbostarter/api/utils";
import { api } from "~/lib/api";
export default function TabOneScreen() {
const { data: posts, isLoading } = useQuery({
queryKey: ["posts"],
queryFn: handle(api.posts.$get),
});
if (isLoading) {
return <Text>Loading...</Text>;
}
/* do something with the data... */
return (
<View>
<Text>{JSON.stringify(posts)}</Text>
</View>
);
}
```
</Tab>
<Tab value="Mutations">
```tsx
// [!code word:handle]
import { handle } from "@turbostarter/api/utils";
import { api } from "~/lib/api/client";
export default function CreatePost() {
const queryClient = useQueryClient();
const { mutate } = useMutation({
mutationFn: handle(api.posts.$post),
onSuccess: () => {
toast.success("Post created successfully!");
queryClient.invalidateQueries({ queryKey: ["posts"] });
},
});
return (
<Form>
<Button onPress={onSubmit(mutate)}>Submit</Button>
</Form>
);
}
```
</Tab>
</Tabs>
With this approach, you can focus on the business logic instead of repeatedly writing code to handle API responses in your browser extension components, making your extension's codebase more readable and maintainable.
The same error handling and response unwrapping benefits apply whether you're building web, mobile, or extension interfaces - allowing you to keep your data fetching logic consistent across all platforms.

View File

@@ -0,0 +1,55 @@
---
title: Overview
description: Get started with the API.
url: /docs/mobile/api/overview
---
# Overview
<Callout type="error" title="API deployment required">
To enable communication between your Expo app and the server in a production environment, the web application with Hono API must be deployed first.
<Cards>
<Card title="API" description="Learn more about the API." href="/docs/web/api/overview" />
<Card title="Web deployment" description="Deploy your web application to production." href="/docs/web/deployment/checklist" />
</Cards>
</Callout>
TurboStarter is designed to be a scalable and production-ready full-stack starter kit. One of its core features is a dedicated and extensible API layer. To enable this in a type-safe manner, we chose [Hono](https://hono.dev) as the API server and client library.
<Callout title="Why Hono?">
Hono is a small, simple, and ultrafast web framework that gives you a way to
define your API endpoints with full type safety. It provides built-in
middleware for common needs like validation, caching, and CORS.
It also
includes an [RPC client](https://hono.dev/docs/guides/rpc) for making
type-safe function calls from the frontend. Being edge-first, it's optimized
for serverless environments and offers excellent performance.
</Callout>
All API endpoints and their resolvers are defined in the `packages/api/` package. Here you will find a `modules` folder that contains the different feature modules of the API. Each module has its own folder and exports all its resolvers.
For each module, we create a separate Hono route in the `packages/api/index.ts` file and aggregate all sub-routers into one main router.
The API is then exposed as a route handler that will be provided as a Next.js API route:
```ts title="apps/web/src/app/api/[...route]/route.ts"
import { handle } from "hono/vercel";
import { appRouter } from "@turbostarter/api";
const handler = handle(appRouter);
export {
handler as GET,
handler as POST,
handler as OPTIONS,
handler as PUT,
handler as PATCH,
handler as DELETE,
handler as HEAD,
};
```
Learn more about how to use the API in your mobile app in the following sections:

View File

@@ -0,0 +1,131 @@
---
title: Two-Factor Authentication (2FA)
description: Add an extra layer of security with two-factor authentication in your mobile app.
url: /docs/mobile/auth/2fa
---
# Two-Factor Authentication (2FA)
TurboStarter uses [Better Auth's 2FA plugin](https://www.better-auth.com/docs/plugins/2fa) to provide multi-factor authentication (MFA) capabilities in your mobile app. Two-factor authentication adds an extra layer of security by requiring users to provide a second form of verification alongside their password.
## Available methods
TurboStarter supports multiple 2FA verification methods through Better Auth:
* **TOTP (Time-based One-Time Password)** - codes generated by authenticator apps
* **OTP (One-Time Password)** - codes sent via email or SMS
* **Backup codes** - single-use recovery codes for account recovery
You can use any TOTP-compatible authenticator app, such as:
* [Google Authenticator](https://support.google.com/accounts/answer/1066447)
* [Authy](https://authy.com/)
* [Microsoft Authenticator](https://www.microsoft.com/en-us/security/mobile-authenticator-app)
* [1Password](https://1password.com/features/authenticator/)
* [Bitwarden](https://bitwarden.com/help/authenticator-keys/)
## Enabling 2FA
<Steps>
<Step>
### Enable in settings
Users enable two-factor authentication in their account security settings within the mobile app.
![Enable 2FA](/images/docs/mobile/auth/two-factor/enable.png)
</Step>
<Step>
### Setup authenticator
A QR code is displayed in the mobile app for users to scan with their authenticator app. Users can also manually enter the setup key if needed.
![Setup authenticator](/images/docs/mobile/auth/two-factor/authenticator-app.png)
</Step>
<Step>
### Verify setup
Users enter a verification code from their authenticator to confirm setup directly in the mobile app.
</Step>
<Step>
### Backup codes
Users receive single-use backup codes for account recovery, which can be saved or shared from the mobile app.
![Backup codes](/images/docs/mobile/auth/two-factor/backup-codes.png)
</Step>
</Steps>
<Callout type="info">
Recovery codes are essential for account recovery if users lose access to
their authenticator device. Make sure to educate users about safely storing
their backup codes, and consider providing options to save them to the device
or share them securely.
</Callout>
## Using 2FA
<Steps>
<Step>
### Sign in normally
Users enter their email and password or use other authentication methods (biometric, social login) as usual in the mobile app.
</Step>
<Step>
### 2FA prompt
After successful password verification, users are prompted for their 2FA code in a native mobile interface.
![2FA prompt](/images/docs/mobile/auth/two-factor/sign-in-prompt.png)
</Step>
<Step>
### Enter verification code
Users input the 6-digit code from their authenticator app using the mobile keyboard.
</Step>
<Step>
### Access granted
Upon successful verification, users gain access to their account and are navigated to the main app screen.
</Step>
</Steps>
### Trusted devices
Users can mark their mobile device as trusted during 2FA verification. Trusted devices won't require 2FA verification for 60 days, providing a balance between security and convenience. This is particularly useful for personal mobile devices.
## Mobile-specific considerations
### Biometric integration
On mobile devices, 2FA can be enhanced with biometric authentication (fingerprint, face recognition) for added security and convenience.
### App switching
The mobile app should handle switching between your app and authenticator apps seamlessly, maintaining the authentication state when users return.
### Offline support
Consider implementing offline backup code verification for scenarios where users may have limited connectivity.
### Push notifications
For OTP delivery via SMS or email, ensure your app handles incoming notifications gracefully during the authentication flow.
## Configuration
2FA is configured through Better Auth's plugin system. The plugin handles:
* Secure secret generation and storage
* QR code generation for authenticator setup
* TOTP code validation
* Backup code generation and management
* Trusted device management
* Mobile-specific session handling
For detailed implementation instructions, refer to the [Better Auth 2FA documentation](https://www.better-auth.com/docs/plugins/2fa).

View File

@@ -0,0 +1,122 @@
---
title: Configuration
description: Configure authentication for your application.
url: /docs/mobile/auth/configuration
---
# Configuration
TurboStarter supports multiple different authentication methods:
* **Password** - the traditional email/password method
* **Magic Link** - passwordless email link authentication
* **Anonymous** - guest mode for users who want to proceed anonymously
* **OAuth** - OAuth providers, [Apple](https://www.better-auth.com/docs/authentication/apple), [Google](https://www.better-auth.com/docs/authentication/google) and [Github](https://www.better-auth.com/docs/authentication/github) are set up by default
All authentication methods are enabled by default, but you can easily customize them to your needs. You can enable or disable any method, and configure them according to your requirements.
<Callout>
Remember that you can mix and match these methods or add new ones - for
example, you can have both password and magic link authentication enabled at
the same time, giving your users more flexibility in how they authenticate.
</Callout>
Authentication configuration can be customized through a simple configuration file. The following sections explain the available options and how to configure each authentication method based on your requirements.
## API
To enable new authentication method or add some plugin, you'd need to update the API configuration. Please refer to [web authentication configuration](/docs/web/auth/configuration) for more information as it's not strictly related with mobile app configuration.
<Callout title="Remember to add your app scheme as trusted origin">
For mobile apps, we need to define an [authentication trusted origin](https://www.better-auth.com/docs/reference/security#trusted-origins) using a mobile app scheme instead.
App schemes (like `turbostarter://`) are used for [deep linking](https://docs.expo.dev/guides/linking/) users to specific screens in your app after authentication.
To find your app scheme, take a look at `apps/mobile/app.config.ts` file and then add it to your auth server configuration:
```ts title="server.ts"
export const auth = betterAuth({
...
trustedOrigins: ["turbostarter://**"],
...
});
```
Adding your app scheme to the trusted origins list is crucial for security - it prevents CSRF attacks and blocks malicious open redirects by ensuring only requests from approved origins (your app) are allowed through.
[Read more about auth security in Better Auth's documentation.](https://www.better-auth.com/docs/reference/security)
</Callout>
## UI
We have separate configuration that determines what is displayed to your users in the **UI**. It's set at `apps/mobile/config/auth.ts`.
The recommendation is to **not update this directly** - instead, please define the environment variables and override the default behavior.
```ts title="apps/mobile/config/auth.ts"
import env from "env.config";
import { Platform } from "react-native";
import { SocialProvider, authConfigSchema } from "@turbostarter/auth";
import type { AuthConfig } from "@turbostarter/auth";
export const authConfig = authConfigSchema.parse({
providers: {
password: env.EXPO_PUBLIC_AUTH_PASSWORD,
magicLink: env.EXPO_PUBLIC_AUTH_MAGIC_LINK,
anonymous: env.EXPO_PUBLIC_AUTH_ANONYMOUS,
oAuth: [
Platform.select({
android: SocialProvider.GOOGLE,
ios: SocialProvider.APPLE,
}),
SocialProvider.GITHUB,
],
},
}) satisfies AuthConfig;
```
The configuration is also validated using the Zod schema, so if something is off, you'll see the errors.
For example, if you want to switch from password to magic link, you'd change the following environment variables:
```dotenv title=".env.local"
EXPO_PUBLIC_AUTH_PASSWORD=false
EXPO_PUBLIC_AUTH_MAGIC_LINK=true
```
To display third-party providers in the UI, you need to set the `oAuth` array to include the provider you want to display. The default is Google and Github.
```tsx title="apps/web/config/auth.ts"
providers: {
...
oAuth: [
Platform.select({
android: SocialProvider.GOOGLE,
ios: SocialProvider.APPLE,
}),
SocialProvider.GITHUB,
],
...
},
```
You can even display specific providers for specific platforms - for example, you can display Google authentication for Android and Apple authentication for iOS.
## Third party providers
To enable third-party authentication providers, you'll need to:
1. Set up an OAuth application in the provider's developer console (like [Apple Developer Portal](https://developer.apple.com/account/), [Google Cloud Console](https://console.cloud.google.com/), [Github Developer Settings](https://github.com/settings/developers) or any other provider you want to use)
2. Configure the corresponding environment variables in your TurboStarter **API (web) application**
Each OAuth provider requires its own set of credentials and environment variables. Please refer to the [Better Auth documentation](https://better-auth.com/docs/concepts/oauth) for detailed setup instructions for each supported provider.
<Callout title="Multiple environments">
Make sure to set both development and production environment variables
appropriately. Your OAuth provider may require different callback URLs for
each environment.
</Callout>

View File

@@ -0,0 +1,51 @@
---
title: User flow
description: Discover the authentication flow in Turbostarter.
url: /docs/mobile/auth/flow
---
# User flow
TurboStarter ships with a fully functional authentication system. Most of the screens and components are preconfigured and easily customizable to your needs.
Here you will find a quick walkthrough of the authentication flow.
## Sign up
The sign-up screen is where users can create an account. They need to provide their email address and password.
![Sign up](/images/docs/mobile/auth/sign-up.png)
Once successful, users are asked to confirm their email address. This is enabled by default - and due to security reasons, it's not possible to disable it.
<Callout type="warn" title="Sending authentication emails">
Make sure to configure the [email provider](/docs/web/emails/configuration) together with the [auth hooks](/docs/web/emails/sending#authentication-emails) to be able to send emails from your app.
</Callout>
![Confirm email](/images/docs/mobile/auth/confirm-email.png)
## Sign in
The sign-in screen is where users can log in to their account. They need to provide their email address and password, use magic link (if enabled) or third-party providers.
![Sign in](/images/docs/mobile/auth/sign-in.png)
## Sign out
The sign out button is located in the user account settings.
![Settings](/images/docs/mobile/auth/settings.png)
## Forgot password
The forgot password screen is where users can reset their password. They need to provide their email address and follow the instructions in the email.
It comes together with the reset password screen, where users land from a forgot email. There they can reset their password by providing new password and confirming it.
![Forgot password](/images/docs/mobile/auth/forgot-password.png)
## Two-factor authentication
Two-factor authentication is a security feature that requires users to provide a code sent to their email or phone number in addition to their password when logging in.
![Two-factor authentication](/images/docs/mobile/auth/two-factor/sign-in-prompt.png)

View File

@@ -0,0 +1,72 @@
---
title: OAuth
description: Get started with social authentication.
url: /docs/mobile/auth/oauth
---
# OAuth
Better Auth supports almost **30** (!) different [OAuth providers](https://www.better-auth.com/docs/concepts/oauth). They can be easily configured and enabled in the kit without any additional configuration needed.
<Callout title="Everything configured!">
TurboStarter provides you with all the configuration required to handle OAuth providers responses from your app:
* redirects
* middleware
* confirmation API routes
You just need to configure one of the below providers on their side and set correct credentials as environment variables in your TurboStarter app.
</Callout>
![OAuth providers](/images/docs/web/auth/social-providers.png)
Third Party providers need to be configured, managed and enabled fully on the provider's side. TurboStarter just needs the correct credentials to be set as environment variables in your app and passed to the [authentication API configuration](/docs/web/auth/configuration#api).
To enable OAuth providers in your TurboStarter app, you need to:
1. Set up an OAuth application in the provider's developer console (like [Apple Developer Portal](https://developer.apple.com/account/), [Google Cloud Console](https://console.cloud.google.com/), [Github Developer Settings](https://github.com/settings/developers) or any other provider you want to use)
2. Configure the provider's credentials as environment variables in your app. For example, for Google OAuth:
```dotenv title="apps/web/.env.local"
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
```
Then, pass it to the authentication configuration in `packages/auth/src/server.ts`:
```ts title="server.ts"
export const auth = betterAuth({
...
socialProviders: {
[SocialProvider.GOOGLE]: {
clientId: env.GOOGLE_CLIENT_ID,
clientSecret: env.GOOGLE_CLIENT_SECRET,
},
},
...
});
```
<Callout title="Remember to add your app scheme as trusted origin">
For mobile apps, we need to define a trusted origin using an app scheme instead of a classic URL. App schemes (like `turbostarter://`) are used for [deep linking](https://docs.expo.dev/guides/linking/) users to specific screens in your app after authentication.
To find your app scheme, take a look at `apps/mobile/app.config.ts` file and then add it to your auth server configuration:
```ts title="server.ts"
export const auth = betterAuth({
...
trustedOrigins: ["turbostarter://**"],
...
});
```
Adding your app scheme to the trusted origins list is crucial for security - it prevents CSRF attacks and blocks malicious open redirects by ensuring only requests from approved origins (your app) are allowed through.
[Read more about auth security in Better Auth's documentation.](https://www.better-auth.com/docs/reference/security)
</Callout>
Also, we included some native integrations (["Sign in with Apple"](/docs/mobile/auth/oauth/apple) for iOS and ["Sign in with Google"](/docs/mobile/auth/oauth/google) for Android) to make the sign-in process smoother and faster for the user.

View File

@@ -0,0 +1,74 @@
---
title: Apple
description: Configure "Sign in with Apple" for your mobile application.
url: /docs/mobile/auth/oauth/apple
---
# Apple
**"Sign in with Apple"** provides a native, privacy-preserving SSO experience on iOS. Use the system Apple button and the Apple Authentication APIs to sign users in, then verify the identity token on your backend and create a session with your auth server.
<Callout title="Apple ID authentication is available on iOS only">
Native Apple ID authentication is available on iOS only. You are advised to
present the official system button (or our custom component - also compliant!)
and follow [Apple's Human Interface
Guidelines](https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple)
for best practices.
</Callout>
![Sign in with Apple](/images/docs/mobile/auth/sign-in-with-apple.png)
## Why use native Apple ID authentication?
<Cards>
<Card title="First-class native UX">
System sheet + official button, aligned with [Apple's Human Interface Guidelines](https://developer.apple.com/design/human-interface-guidelines/sign-in-with-apple) for trust and conversion.
</Card>
<Card title="Privacy-forward">
Private relay email and limited data by design, ensuring your users' privacy is protected and compliant with App Store guidelines.
</Card>
<Card title="Fewer passwords">
Fast, low-friction sign-in on iOS enabling your users to sign in without the need to remember or create additional passwords.
</Card>
<Card title="Secure by default">
JWT verification on the server with [Better Auth](https://www.better-auth.com/docs/authentication/apple), keeping your users' credentials secure.
</Card>
<Card title="Seamless sessions">
We exchange Apple credentials for an app session and persist it in the app.
</Card>
</Cards>
## Requirements
* Enable the "Sign in with Apple" capability for your bundle identifier in the [Apple Developer Portal](https://developer.apple.com/account/resources/identifiers/list)
* Add the entitlement and build with [EAS](/docs/mobile/publishing/checklist) (or configure natively)
* Ensure your app's deep link scheme is added to the auth server's [trusted origins configuration](/docs/mobile/auth/configuration)
Check the [Better Auth documentation](https://www.better-auth.com/docs/authentication/apple) for more details on how to configure all the required keys and certificates.
## High-level flow
1. Check availability with `AppleAuthentication.isAvailableAsync()`.
2. Render the system `AppleAuthenticationButton` or custom TurboStarter component.
3. Call `AppleAuthentication.signInAsync()` requesting `FULL_NAME` and/or `EMAIL` as needed.
4. Send the returned `idTokeb` identifier to the API powered by [Better Auth](https://www.better-auth.com/docs/authentication/apple) to verify and establish a session.
5. Optionally track credential state with `AppleAuthentication.getCredentialStateAsync(user)`.
<Callout type="warn" title="Verify on the server">
Always verify the JWT signature from `idToken` on your backend using Apple's
public keys before creating a session.
</Callout>
For a more in-depth overview of Apple ID authentication—including implementation details, platform caveats, and advanced configuration—see the following resources:
<Cards>
<Card title="Expo AppleAuthentication" href="https://docs.expo.dev/versions/latest/sdk/apple-authentication/" description="docs.expo.dev" />
<Card title="Login with Apple" href="https://www.better-auth.com/docs/authentication/apple" description="better-auth.com" />
<Card title="Sign in with Apple" href="https://developer.apple.com/documentation/sign_in_with_apple" description="developer.apple.com" />
</Cards>

View File

@@ -0,0 +1,71 @@
---
title: Google
description: Configure "Sign in with Google" for your mobile application.
url: /docs/mobile/auth/oauth/google
---
# Google
**"Sign in with Google"** enables a fast account-chooser experience on mobile (especially on Android). Configure your platform credentials, prompt the native account picker, then exchange the returned token on your backend to create a session with your auth server.
<Callout title="Platform support">
On Android, Google SignIn uses [Google Identity
Services](https://developers.google.com/identity?hl=pl) and integrates with
the system account chooser. On iOS, the recommended Expo flow uses
[expo-auth-session](https://docs.expo.dev/versions/latest/sdk/auth-session/)
with Google for a native, web-based sign-in experience.
</Callout>
![Sign in with Google](/images/docs/mobile/auth/sign-in-with-google.png)
## Why use Google authentication?
<Cards>
<Card title="First-class native UX">
Account picker and token storage integrated with the OS for speed and familiarity.
</Card>
<Card title="Seamless across platforms">
Android native chooser; iOS polished experience via Expo.
</Card>
<Card title="Secure by default">
Tokens are verified server-side with [Better Auth](https://www.better-auth.com/docs/authentication/google) before a session is issued.
</Card>
<Card title="Faster onboarding">
Reduce friction with one-tap sign-in and fewer passwords to remember.
</Card>
<Card title="Scalable">
Built on [Google Identity Services](https://developers.google.com/identity?hl=pl) and best-practice OAuth flows.
</Card>
</Cards>
## Requirements
* Configure [Google Cloud OAuth Client IDs](https://react-native-google-signin.github.io/docs/setting-up/get-config-file) (Android package + SHA-1, iOS bundle ID) in the [Google Cloud Console](https://console.cloud.google.com/)
* Build with [EAS](/docs/mobile/publishing/checklist) to ensure native credentials are embedded correctly
* Add your app deep link scheme to the auth server's [trusted origins configuration](/docs/mobile/auth/configuration)
Check the [Better Auth documentation](https://www.better-auth.com/docs/authentication/google) and [`@react-native-google-signin/google-signin` documentation](https://react-native-google-signin.github.io) for steps to configure your server verification, client IDs and more.
## High-level flow
1. Configure Google OAuth Client IDs for Android and iOS in [Google Cloud Console](https://console.cloud.google.com/).
2. Initialize the Google auth request in your app and render a "Sign in with Google" button.
3. Prompt the account chooser; on success you receive an `idToken` and/or `accessToken`.
4. Send the tokens to the API powered by [Better Auth](https://www.better-auth.com/docs/authentication/google) to verify and establish a session.
5. Persist the session and proceed to the app.
For a more in-depth overview of Google authentication, including implementation details, platform caveats, and advanced configuration, see the following resources:
<Cards>
<Card title="Use Google Authentication" href="https://docs.expo.dev/guides/google-authentication/" description="docs.expo.dev" />
<Card title="Login with Google" href="https://www.better-auth.com/docs/authentication/google" description="better-auth.com" />
<Card title="React Native Google Sign In" href="https://react-native-google-signin.github.io/" description="react-native-google-signin.github.io" />
<Card title="Authenticate users with Sign in with Google" href="https://developer.android.com/identity/sign-in/credential-manager-siwg" description="developer.android.com" />
</Cards>

View File

@@ -0,0 +1,40 @@
---
title: Overview
description: Get started with authentication.
url: /docs/mobile/auth/overview
---
# Overview
TurboStarter uses [Better Auth](https://better-auth.com) to handle authentication. It's a secure, production-ready authentication solution that integrates seamlessly with many frameworks and provides enterprise-grade security out of the box.
<Callout title="Why Better Auth?">
One of the core principles of TurboStarter is to do things **as simple as possible**, and to make everything **as performant as possible**.
Better Auth provides an excellent developer experience with minimal configuration required, while maintaining enterprise-grade security standards. Its framework-agnostic approach and focus on performance makes it the perfect choice for TurboStarter.
Recently, Better Auth [announced](https://www.better-auth.com/blog/authjs-joins-better-auth) an incorporation of [Auth.js (27k+ stars on Github)](https://authjs.dev/), making it even more powerful and flexible.
</Callout>
![Better Auth](/images/docs/better-auth.png)
You can read more about Better Auth in the [official documentation](https://better-auth.com/docs).
TurboStarter supports multiple authentication methods:
* **Password** - the traditional email/password method
* **Magic Link** - magic links with [deep linking](https://docs.expo.dev/linking/overview)
* **Anonymous** - allowing users to proceed anonymously
* **OAuth** - OAuth social providers ([Apple](https://www.better-auth.com/docs/authentication/apple), [Google](https://www.better-auth.com/docs/authentication/google) and [Github](https://www.better-auth.com/docs/authentication/github) preconfigured)
* **Native Apple authentication** - ["Sign in with Apple"](/docs/mobile/auth/oauth/apple) for iOS
* **Native Google authentication** - ["Sign in with Google"](/docs/mobile/auth/oauth/google) for Android
As well as common applications flows, with ready-to-use views and components:
* **Sign in** - sign in with email/password or OAuth providers
* **Sign up** - sign up with email/password or OAuth providers
* **Sign out** - sign out
* **Password recovery** - forgot and reset password
* **Email verification** - verify email
You can construct your auth flow like LEGO bricks - plug in needed parts and customize them to your needs.

View File

@@ -0,0 +1,72 @@
---
title: Billing
description: Get started with billing in TurboStarter.
url: /docs/mobile/billing
---
# Billing
<Callout title="Fully-featured billing on mobile is coming soon">
For now, billing has a limited functionalities on mobile, we're mostly relying on the [web app](/docs/web/billing/overview) to handle billing.
We are working on a fully-featured mobile billing to help you monetize your mobile app easier. Stay tuned for updates.
[See roadmap](https://github.com/orgs/turbostarter/projects/1)
</Callout>
## Fetching customer data
When your user purchased a plan from your landing page or web app, you can easily fetch their data using the [API](/docs/mobile/api/client).
To do so, just call the `/api/billing/customer` endpoint:
```tsx title="customer-screen.tsx"
import { api } from "~/lib/api";
export default function CustomerScreen() {
const { data: customer, isLoading } = useQuery({
queryKey: ["customer"],
queryFn: handle(api.billing.customer.$get),
});
if (isLoading) return <Text>Loading...</Text>;
return <Text>{customer?.plan}</Text>;
}
```
You may also want to ensure that user is logged in before fetching their billing data to avoid unnecessary API calls.
```tsx title="customer-screen.tsx"
import { api } from "~/lib/api";
import { authClient } from "~/lib/auth";
export default function CustomerScreen() {
const {
data: { user },
} = authClient.useSession();
const { data: customer } = useQuery({
queryKey: ["customer"],
queryFn: handle(api.billing.customer.$get),
enabled: !!user, // [!code highlight]
});
if (!user || !customer) {
return null;
}
return (
<View>
<Text>{user.email}</Text>
<Text>{customer.plan}</Text>
</View>
);
}
```
<Callout title="Be cautious!" type="warn">
Be mindful when implementing payment-related features in your mobile app. Apple has strict guidelines regarding external payment systems and **may reject your app** if you aggressively redirect users to web-based payment flows. Make sure to review the [App Store Review Guidelines](https://developer.apple.com/app-store/review/guidelines/#payments) carefully and consider implementing native in-app purchases for iOS users to ensure compliance.
We are currently working on a fully native payments system that will make it easier to comply with Apple's guidelines - stay tuned for updates!
</Callout>

View File

@@ -0,0 +1,92 @@
---
title: CLI
description: Start your new project with a single command.
url: /docs/mobile/cli
---
# CLI
<CliDemo />
To help you get started with TurboStarter **as quickly as possible**, we've developed a [CLI](https://www.npmjs.com/package/@turbostarter/cli) that enables you to create a new project (with all the configuration) in seconds.
The CLI is a set of commands that will help you create a new project, generate code, and manage your project efficiently.
Currently, the following action is available:
* **Starting a new project** - Generate starter code for your project with all necessary configurations in place (billing, database, emails, etc.)
**The CLI is in beta**, and we're actively working on adding more commands and actions. Soon, the following features will be available:
* **Translations** - Translate your project, verify translations, and manage them effectively
* **Installing plugins** - Easily install plugins for your project
* **Dynamic code generation** - Generate dynamic code based on your project structure
## Installation
You can run commands using `npx`:
```bash
npx turbostarter <command>
npx @turbostarter/cli@latest <command>
```
<Callout>
If you don't want to install the CLI globally, you can simply replace the examples below with `npx @turbostarter/cli@latest` instead of `turbostarter`.
This also allows you to always run the latest version of the CLI without having to update it.
</Callout>
## Usage
Running the CLI without any arguments will display the general information about the CLI:
```bash
Usage: turbostarter [options] [command]
Your Turbo Assistant for starting new projects, adding plugins and more.
Options:
-v, --version display the version number
-h, --help display help for command
Commands:
new create a new TurboStarter project
help [command] display help for command
```
You can also display help for it or check the actual version.
### Starting a new project
Use the `new` command to initialize configuration and dependencies for a new project.
```bash
npx turbostarter new
```
You will be asked a few questions to configure your project:
```bash
✔ All prerequisites are satisfied, let's start! 🚀
? What do you want to ship?
◉ Web app
◉ Mobile app
◯ Browser extension
? Enter your project name.
? How do you want to use database?
Local (powered by Docker)
Cloud
? What do you want to use for billing?
Stripe
Lemon Squeezy
...
🎉 You can now get started. Open the project and just ship it! 🎉
Problems? https://www.turbostarter.dev/docs
```
It will create a new project, configure providers, install dependencies and start required services in development mode.

View File

@@ -0,0 +1,152 @@
---
title: App configuration
description: Learn how to setup the overall settings of your app.
url: /docs/mobile/configuration/app
---
# App configuration
When configuring your app, you'll need to define settings in different places depending on which provider will use them (e.g., Expo, EAS).
## App configuration
Let's start with the core settings for your app. These settings are **crucial** as they're used by Expo and EAS to build your app, determine its store presence, prepare updates, and more.
This configuration includes essential details like the official name, description, scheme, store IDs, splash screen configuration, and more.
You'll define these settings in `apps/mobile/app.config.ts`. Make sure to follow the [Expo config schema](https://docs.expo.dev/versions/latest/config/app/) when setting this up.
Here is an example of what the config file looks like:
```ts title="apps/mobile/app.config.ts"
import { ExpoConfig } from "expo/config";
export default ({ config }: ConfigContext): ExpoConfig => ({
...config,
name: "TurboStarter",
slug: "turbostarter",
scheme: "turbostarter",
version: "0.1.0",
orientation: "portrait",
icon: "./assets/images/icon.png",
userInterfaceStyle: "automatic",
assetBundlePatterns: ["**/*"],
sdkVersion: "51.0.0",
platforms: ["ios", "android"],
updates: {
fallbackToCacheTimeout: 0,
},
newArchEnabled: true,
ios: {
bundleIdentifier: "your.bundle.identifier",
supportsTablet: false,
},
android: {
package: "your.bundle.identifier",
adaptiveIcon: {
monochromeImage: "./public/images/icon/android/monochrome.png",
foregroundImage: "./public/images/icon/android/adaptive.png",
backgroundColor: "#0D121C",
},
},
extra: {
eas: {
projectId: "your-project-id",
},
},
experiments: {
tsconfigPaths: true,
typedRoutes: true,
},
plugins: ["expo-router", ["expo-splash-screen", SPLASH]],
});
```
Make sure to replace the values with your own and take your time to set everything correctly.
<Card title="Configure with app config" description="docs.expo.dev" href="https://docs.expo.dev/workflow/configuration/" />
### Internal configuration
The same as for the [web app](/docs/web/configuration/app), and [extension](/docs/extension/configuration/app), we're defining the internal app config, which stores some overall variables for your application (that can't be read from Expo config).
The recommendation is to **not update this directly** - instead, please define the environment variables and override the default behavior. The configuration is strongly typed so you can use it safely accross your codebase - it'll be validated at build time.
```ts title="apps/mobile/src/config/app.ts"
import env from "env.config";
export const appConfig = {
locale: env.EXPO_PUBLIC_DEFAULT_LOCALE,
url: env.EXPO_PUBLIC_SITE_URL,
theme: {
mode: env.EXPO_PUBLIC_THEME_MODE,
color: env.EXPO_PUBLIC_THEME_COLOR,
},
} as const;
```
For example, to set the mobile app default theme color, you'd update the following variable:
```dotenv title=".env.local"
EXPO_PUBLIC_THEME_COLOR="yellow"
```
<Callout type="warn" title="Do NOT use process.env!">
Do NOT use `process.env` to get the values of the variables. Variables
accessed this way are not validated at build time, and thus the wrong variable
can be used in production.
</Callout>
## EAS configuration
To properly build and publish your app, you need to define settings for the EAS build service.
This is done in `apps/mobile/eas.json` and it must follow the [EAS config scheme](https://docs.expo.dev/eas/json/).
Here is an example of what the config file looks like:
```json title="apps/mobile/eas.json"
{
"cli": {
"version": ">= 4.1.2"
},
"build": {
"base": {
"node": "20.15.0",
"pnpm": "9.6.0",
"ios": {
"resourceClass": "m-medium"
},
"env": {
"EXPO_PUBLIC_DEFAULT_LOCALE": "en",
"EXPO_PUBLIC_AUTH_PASSWORD": "true",
"EXPO_PUBLIC_AUTH_MAGIC_LINK": "false",
"EXPO_PUBLIC_THEME_MODE": "system",
"EXPO_PUBLIC_THEME_COLOR": "orange"
}
},
...
"preview": {
"extends": "base",
"distribution": "internal",
"android": {
"buildType": "apk"
},
"env": {
"APP_ENV": "test",
}
},
"production": {
"extends": "base",
"env": {
"APP_ENV": "production",
}
}
...
},
}
```
Make sure to also fill all the [environment variables](/docs/mobile/configuration/environment-variables) with the correct values for your project and correct environment, otherwise your app won't build and you won't be able to publish it.
<Card title="Configure EAS Build with eas.json" description="docs.expo.dev" href="https://docs.expo.dev/build/eas-json/" />

View File

@@ -0,0 +1,92 @@
---
title: Environment variables
description: Learn how to configure environment variables.
url: /docs/mobile/configuration/environment-variables
---
# Environment variables
Environment variables are defined in the `.env` file in the root of the repository and in the root of the `apps/mobile` package.
* **Shared environment variables**: Defined in the **root** `.env` file. These are shared between environments (e.g., development, staging, production) and apps (e.g., web, mobile).
* **Environment-specific variables**: Defined in `.env.development` and `.env.production` files. These are specific to the development and production environments.
* **App-specific variables**: Defined in the app-specific directory (e.g., `apps/web`). These are specific to the app and are not shared between apps.
* **Build environment variables**: Not stored in the `.env` file. Instead, they are stored in `eas.json` file used to build app on [Expo Application Services](https://expo.dev/eas).
* **Secret keys**: They're not stored on mobile side, instead [they're defined on the web side.](/docs/web/configuration/environment-variables#secret-keys)
## Shared variables
Here you can add all the environment variables that are shared across all the apps.
To override these variables in a specific environment, please add them to the specific environment file (e.g. `.env.development`, `.env.production`).
```dotenv title=".env.local"
# Shared environment variables
# The database URL is used to connect to your database.
DATABASE_URL="postgresql://postgres:postgres@localhost:5432/postgres"
# The name of the product. This is used in various places across the apps.
PRODUCT_NAME="TurboStarter"
# The url of the web app. Used mostly to link between apps.
URL="http://localhost:3000"
...
```
## App-specific variables
Here you can add all the environment variables that are specific to the app (e.g. `apps/mobile`).
You can also override the shared variables defined in the root `.env` file.
```dotenv title="apps/mobile/.env.local"
# App-specific environment variables
# Env variables extracted from shared to be exposed to the client in Expo app
EXPO_PUBLIC_SITE_URL="${URL}"
EXPO_PUBLIC_DEFAULT_LOCALE="${DEFAULT_LOCALE}"
# Theme mode and color
EXPO_PUBLIC_THEME_MODE="system"
EXPO_PUBLIC_THEME_COLOR="orange"
# Use this variable to enable or disable password-based authentication. If you set this to true, users will be able to sign up and sign in using their email and password. If you set this to false, the form won't be shown.
EXPO_PUBLIC_AUTH_PASSWORD="true"
...
```
<Callout title="EXPO_PUBLIC_ prefix">
To make environment variables available in the Expo app code, you need to prefix them with `EXPO_PUBLIC_`. They will be injected to the code during the build process.
Only environment variables prefixed with `EXPO_PUBLIC_` will be injected.
[Read more about Expo environment variables.](https://docs.expo.dev/guides/environment-variables/)
</Callout>
## Build environment variables
To allow your app to build properly on [EAS](https://expo.dev/eas) you need to define your environment variables either in your `eas.json` file under corresponding profile (e.g. `preview` or `production`) or directly in the [EAS platform](https://docs.expo.dev/eas/environment-variables/):
![EAS environment variables](/images/docs/mobile/eas-environment-variables.png)
Then, when you trigger build, correct environment variables will be injected to your mobile app code ensuring that everything is working correctly.
[Check EAS documentation for more details.](https://docs.expo.dev/eas/environment-variables/)
## Secret keys
Secret keys and sensitive information are to be **never** stored on the mobile app code.
<Callout title="What does this mean?">
It means that you will need to add the secret keys to the **web app, where the API is deployed.**
The mobile app should only communicate with the backend API, which is typically part of the web app. The web app is responsible for handling sensitive operations and storing secret keys securely.
[See web documentation for more details.](/docs/web/configuration/environment-variables#secret-keys)
This is not a TurboStarter-specific requirement, but a best practice for security for any
application. Ultimately, it's your choice.
</Callout>

View File

@@ -0,0 +1,47 @@
---
title: Paths configuration
description: Learn how to configure the paths of your app.
url: /docs/mobile/configuration/paths
---
# Paths configuration
The paths configuration is set at `apps/mobile/config/paths.ts`. This configuration stores all the paths that you'll be using in your application. It is a convenient way to store them in a central place rather than scatter them in the codebase using magic strings.
It is **unlikely you'll need to change** this unless you're heavily editing the codebase.
```ts title="apps/mobile/config/paths.ts"
const pathsConfig = {
index: "/",
setup: {
welcome: "/welcome",
auth: {
login: `${AUTH_PREFIX}/login`,
register: `${AUTH_PREFIX}/register`,
forgotPassword: `${AUTH_PREFIX}/password/forgot`,
updatePassword: `${AUTH_PREFIX}/password/update`,
error: `${AUTH_PREFIX}/error`,
join: `${AUTH_PREFIX}/join`,
},
steps: {
start: `${STEPS_PREFIX}/start`,
required: `${STEPS_PREFIX}/required`,
skip: `${STEPS_PREFIX}/skip`,
final: `${STEPS_PREFIX}/final`,
},
},
dashboard: {
user: {
index: DASHBOARD_PREFIX,
ai: `${DASHBOARD_PREFIX}/ai`,
...
}
...
}
} as const;
```
<Callout title="Fully type-safe">
By declaring the paths as constants, we can use them safely throughout the
codebase. There is no risk of misspelling or using magic strings.
</Callout>

Some files were not shown because too many files have changed in this diff Show More