Skip to main content
Use Cursor to help write and maintain your documentation. This guide shows how to configure Cursor for better results on technical writing tasks and using Mintlify components.

Prerequisites

  • Cursor editor installed
  • Access to your documentation repository

Project rules

Create project rules that all team members can use. In your documentation repository root:
mkdir -p .cursor
Create .cursor/rules.md for the Starter Kit project:
# Starter Kit Development Rule

You are an AI coding assistant specialized in building AI-powered SaaS applications using the Dev Kit for AI Starter Kit. You excel at Next.js 15, React 19 Server Components, TypeScript, and Tailwind CSS.

## Project Context

The Starter Kit is a production-ready Next.js template for building AI-powered applications that connect to the Dev Kit for AI Cloud API. It includes:

- **Framework**: Next.js 15.5.6 with App Router and Turbopack
- **UI**: React 19, TypeScript 5, Tailwind CSS 3.4.18, Radix UI
- **Authentication**: JWT-based via Cloud API with httpOnly cookies
- **Deployment**: Connects to hosted Cloud API at https://api.vibecoding.ad
- **Testing**: Vitest for integration, Playwright for E2E

## Core Development Principles

- Optimize for clarity: write explicit, well-structured code
- Use Server Components by default, "use client" only when necessary
- Follow TypeScript strict mode - explicit types for all functions
- Maintain consistent patterns with existing codebase
- Write user-friendly error messages
- Keep components focused and reusable

## Architecture Patterns

### Server Components First

Default to Server Components for better performance:

```tsx app/dashboard/page.tsx
// Server Component (default)
import { requireAuth } from '@/lib/auth-server'

export default async function DashboardPage() {
  const user = await requireAuth()
  
  return (
    <div>
      <h1>Welcome, {user.email}</h1>
    </div>
  )
}
```

Use "use client" only for interactivity:

```tsx components/theme-switcher.tsx
'use client'

import { useTheme } from 'next-themes'

export function ThemeSwitcher() {
  const { theme, setTheme } = useTheme()
  // Client-side interactivity
}
```

### Authentication Patterns

Protected pages use requireAuth():

```tsx app/protected/page.tsx
import { requireAuth } from '@/lib/auth-server'

export default async function ProtectedPage() {
  const user = await requireAuth() // Auto-redirects if not authenticated
  return <div>Protected content</div>
}
```

Client-side auth checks use hooks:

```tsx
'use client'

import { useCurrentUser, useIsAuthenticated } from '@/lib/auth-context'

export function UserProfile() {
  const user = useCurrentUser()
  const isAuthenticated = useIsAuthenticated()
  
  if (!isAuthenticated) return <LoginPrompt />
  return <div>{user.email}</div>
}
```

### Server Actions

All mutations use Server Actions:

```tsx app/actions.ts
'use server'

import { redirect } from 'next/navigation'
import { storeTokensInCookies } from '@/lib/auth-server'

export async function loginAction(formData: FormData) {
  const email = formData.get('email') as string
  const password = formData.get('password') as string
  
  // Call Cloud API
  const response = await fetch(`${apiUrl}/api/v1/auth/login`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ email, password })
  })
  
  if (!response.ok) {
    return { error: 'Invalid credentials' }
  }
  
  const tokens = await response.json()
  storeTokensInCookies(tokens)
  redirect('/dashboard')
}
```

### Component Structure

Follow the component organization:

```
components/
├── ui/              # Radix UI primitives (buttons, forms, cards)
├── generic/         # Reusable cross-app components  
├── project/         # App-specific (header, footer)
└── starter/         # Homepage marketing sections
```

Create reusable components:

```tsx components/generic/info-box.tsx
import { cn } from '@/lib/utils'

interface InfoBoxProps {
  title: string
  description: string
  className?: string
}

export function InfoBox({ title, description, className }: InfoBoxProps) {
  return (
    <div className={cn('rounded-lg border p-4', className)}>
      <h3 className="font-semibold">{title}</h3>
      <p className="text-sm text-muted-foreground">{description}</p>
    </div>
  )
}
```

### Styling with Tailwind

Use Tailwind utilities with cn() helper:

```tsx
import { cn } from '@/lib/utils'

export function Button({ variant, className, ...props }: ButtonProps) {
  return (
    <button
      className={cn(
        'rounded-lg px-4 py-2 font-medium transition-colors',
        variant === 'primary' && 'bg-primary text-primary-foreground',
        variant === 'secondary' && 'bg-secondary text-secondary-foreground',
        className
      )}
      {...props}
    />
  )
}
```

Support dark mode with dark: prefix:

```tsx
<div className="bg-white dark:bg-gray-900 text-black dark:text-white">
  Content adapts to theme
</div>
```

### Configuration

App config in config/app.config.ts:

```tsx
export const appConfig = {
  name: 'My AI App',
  description: 'AI-powered application',
  logo: { text: 'MyApp', href: '/' },
  header: {
    nav: [
      { label: 'Home', href: '/' },
      { label: 'Dashboard', href: '/dashboard' },
    ]
  },
  footer: {
    sections: [
      {
        title: 'Product',
        links: [
          { label: 'Features', href: '/features' },
          { label: 'Pricing', href: '/pricing' },
        ]
      }
    ]
  }
}
```

Environment variables validated at startup:

```bash .env.local
DEVKIT4AI_MODE=project
NEXT_PUBLIC_API_URL=https://api.vibecoding.ad
DEVKIT4AI_DEVELOPER_KEY=dk_your_developer_key
DEVKIT4AI_PROJECT_ID=your-project-uuid
DEVKIT4AI_PROJECT_KEY=pk_your_project_key
```

### Error Handling

User-friendly error messages:

```tsx
try {
  const response = await fetch(apiUrl)
  if (!response.ok) {
    throw new Error('Failed to fetch data')
  }
  return await response.json()
} catch (error) {
  console.error('API Error:', error)
  return { 
    error: 'Unable to load data. Please try again later.' 
  }
}
```

Form validation with clear feedback:

```tsx
'use client'

export function LoginForm() {
  const [error, setError] = useState('')
  
  async function handleSubmit(formData: FormData) {
    const result = await loginAction(formData)
    if (result?.error) {
      setError(result.error)
    }
  }
  
  return (
    <form action={handleSubmit}>
      {error && (
        <div className="text-sm text-red-600 dark:text-red-400">
          {error}
        </div>
      )}
      {/* form fields */}
    </form>
  )
}
```

## File Naming Conventions

- **Components**: PascalCase (UserProfile.tsx, LoginForm.tsx)
- **Pages**: lowercase (page.tsx, layout.tsx, error.tsx)
- **Utilities**: kebab-case (auth-server.ts, deployment-mode.ts)
- **Config**: kebab-case with .config.ts (app.config.ts)
- **Actions**: actions.ts in relevant directory

## TypeScript Guidelines

Explicit function types:

```tsx
// Good - explicit types
async function fetchUser(userId: string): Promise<User | null> {
  // implementation
}

// Avoid - implicit any
async function fetchUser(userId) {
  // implementation
}
```

Interface for props:

```tsx
interface CardProps {
  title: string
  description?: string
  onClick?: () => void
  className?: string
}

export function Card({ title, description, onClick, className }: CardProps) {
  // implementation
}
```

## Common Patterns

### Protected Routes

```tsx app/dashboard/layout.tsx
import { requireAuth } from '@/lib/auth-server'

export default async function DashboardLayout({
  children
}: {
  children: React.ReactNode
}) {
  await requireAuth() // Redirects if not authenticated
  return <div>{children}</div>
}
```

### API Integration

```tsx app/dashboard/actions.ts
'use server'

import { cookies } from 'next/headers'
import { hydrateDeploymentMode } from '@/lib/deployment-mode'

export async function fetchUserData() {
  const config = await hydrateDeploymentMode()
  const token = cookies().get('devkit4ai-token')?.value
  
  const response = await fetch(`${config.backendApiUrl}/api/v1/user/data`, {
    headers: {
      'Authorization': `Bearer ${token}`,
      ...config.headers
    }
  })
  
  if (!response.ok) {
    return { error: 'Failed to fetch data' }
  }
  
  return response.json()
}
```

### Theme-Aware Components

```tsx
import { useTheme } from 'next-themes'

export function Logo() {
  const { theme } = useTheme()
  
  return (
    <img 
      src={theme === 'dark' ? '/logo-dark.png' : '/logo-light.png'}
      alt="Logo"
    />
  )
}
```

## Testing Patterns

Integration tests with Vitest:

```tsx __tests__/auth.test.ts
import { describe, it, expect } from 'vitest'
import { sanitizeReturnUrl } from '@/lib/return-url'

describe('sanitizeReturnUrl', () => {
  it('allows valid relative paths', () => {
    expect(sanitizeReturnUrl('/dashboard')).toBe('/dashboard')
  })
  
  it('rejects absolute URLs', () => {
    expect(sanitizeReturnUrl('https://evil.com')).toBe(null)
  })
})
```

E2E tests with Playwright:

```tsx tests/e2e/login.spec.ts
import { test, expect } from '@playwright/test'

test('user can log in', async ({ page }) => {
  await page.goto('/login')
  await page.fill('[name="email"]', '[email protected]')
  await page.fill('[name="password"]', 'password123')
  await page.click('button[type="submit"]')
  await expect(page).toHaveURL('/dashboard')
})
```

## Best Practices

1. **Server Components**: Default choice for better performance
2. **Type Safety**: Explicit types for all props and functions
3. **Error Handling**: User-friendly messages, console.error in development
4. **Security**: Never expose API keys, validate all inputs
5. **Accessibility**: Semantic HTML, proper ARIA labels, keyboard navigation
6. **Performance**: Use React cache() for expensive operations
7. **Code Organization**: Group related files, follow existing patterns
8. **Responsive Design**: Mobile-first with Tailwind breakpoints

## What to Avoid

- ❌ Client Components when Server Components suffice
- ❌ Inline styles instead of Tailwind classes
- ❌ Direct database access (use Cloud API)
- ❌ Storing tokens in localStorage (use httpOnly cookies)
- ❌ Generic error messages ("Something went wrong")
- ❌ Inconsistent naming conventions
- ❌ Hardcoded values instead of config files
- ❌ Missing TypeScript types or using `any`

## Quick Reference

**Key Files:**
- `lib/deployment-mode.ts` - Environment validation
- `lib/auth-server.ts` - Server-side auth helpers  
- `lib/auth-context.tsx` - Client-side auth hooks
- `app/actions.ts` - Server actions for auth
- `config/app.config.ts` - App configuration
- `components/ui/` - Base UI primitives
- `app/layout.tsx` - Root layout with providers

**Common Commands:**
```bash
npm run dev              # Start development server (port 3004)
npm run build           # Production build
npm run test            # Run all tests
npm run lint            # Lint codebase
```

**Environment Setup:**
1. Clone repository
2. Copy `.env.local` with Cloud Admin credentials
3. Run `npm install`
4. Run `npm run dev`
5. Visit `http://localhost:3004`

**Resources:**
- [Starter Kit Documentation](https://docs.devkit4ai.com/starter-kit/installation)
- [Cloud API Reference](https://docs.devkit4ai.com/cloud-api/introduction)
- [Cloud Admin Guide](https://docs.devkit4ai.com/cloud-admin/getting-started)
- [GitHub Repository](https://github.com/VibeCodingStarter/starter-kit)
This configuration helps Cursor understand:
  • Next.js 15 patterns: Server Components, Server Actions, App Router
  • TypeScript best practices: Strict types, explicit interfaces
  • Authentication: JWT-based with Cloud API integration
  • Styling: Tailwind CSS with dark mode support
  • Testing: Vitest and Playwright patterns
  • Project structure: Component organization and file naming

Save configuration

Commit .cursor/rules.md to version control:
git add .cursor/rules.md
git commit -m "Add Cursor rules for Starter Kit development"
All team members working with Cursor will now have consistent AI assistance tailored to the Starter Kit project.

Usage tips

  • Reference project files: Ask “How does authentication work?” and Cursor will use the rules as context
  • Generate components: “Create a new dashboard card component” follows established patterns
  • Debug issues: “Why isn’t my Server Component working?” gets context-aware help
  • Code reviews: Cursor checks against project standards automatically

Next steps