invision-ai / rules
Cursor rule1 starsChanged 10 months agoMITView on GitHub
Guide d'initialisation d'un projet T3 : configuration, structure, dépendances et bonnes pratiques.
- Reads credentials
- Installs packages
Files
project_init.mdc
---
description: "Guide d'initialisation d'un projet T3 : configuration, structure, dépendances et bonnes pratiques."
globs:
- "package.json"
- "next.config.*"
- "tsconfig.json"
- "tailwind.config.*"
- ".env*"
- "prisma/schema.prisma"
alwaysApply: false
---
# Guide d'Initialisation Projet T3
## Stack Technique
- **Framework** : Next.js 15+ + App Router
- **Language** : TypeScript (strict)
- **API** : tRPC (type-safe)
- **Styling** : Tailwind CSS + shadcn/ui
- **State** : Zustand + Nuqs + tRPC React Query
- **Forms** : React Hook Form + Zod
- **i18n** : next-intl
- **Dates** : date-fns
- **Auth** : Better Auth
- **DB** : Prisma + PostgreSQL
- **Icons** : Lucide React
---
## 1. Création du Projet
### Initialisation T3
```bash
npm create t3-app@latest my-app
cd my-app
```
### Options recommandées
- ✅ TypeScript
- ✅ Tailwind CSS
- ✅ tRPC
- ✅ Better Auth
- ✅ Prisma
- ✅ PostgreSQL
- ✅ App Router
- ✅ ESLint/Prettier
- ✅ Import alias `~/`
---
## 2. Installation des Dépendances Additionnelles
### UI et Styling
```bash
# shadcn/ui
npx shadcn@latest init
# Composants essentiels
npx shadcn@latest add button input label card badge dialog dropdown-menu form select textarea toast skeleton
# Utilitaires
npm install lucide-react class-variance-authority clsx tailwind-merge
```
### State Management
```bash
npm install zustand nuqs
```
### Formulaires
```bash
npm install react-hook-form @hookform/resolvers
```
### i18n et Dates
```bash
npm install next-intl date-fns
```
---
## 3. Structure du Projet
### Créer la Structure
```bash
mkdir -p src/{components/{ui,features,layout},hooks,stores,lib/{utils,validations}}
```
### Structure Finale
```
src/
├── app/
│ ├── (auth)/
│ │ ├── login/
│ │ │ └── page.tsx
│ │ └── layout.tsx
│ ├── (dashboard)/
│ │ ├── dashboard/
│ │ │ └── page.tsx
│ │ └── layout.tsx
│ ├── api/
│ │ ├── auth/
│ │ │ └── [...all]/
│ │ │ └── route.ts
│ │ └── trpc/
│ │ └── [trpc]/
│ │ └── route.ts
│ ├── layout.tsx
│ └── page.tsx
├── components/
│ ├── ui/ # shadcn/ui components
│ ├── features/ # Composants par feature
│ └── layout/ # Composants de layout
├── hooks/ # Custom hooks
├── stores/ # Zustand stores
├── lib/
│ ├── utils.ts # Utilitaires (cn, etc.)
│ └── validations/ # Schémas Zod
├── server/
│ ├── api/
│ │ ├── routers/
│ │ │ └── post.ts
│ │ ├── root.ts
│ │ └── trpc.ts
│ ├── better-auth/
│ │ └── config.ts
│ └── db.ts
├── trpc/
│ ├── react.tsx
│ ├── server.ts
│ └── query-client.ts
└── styles/
└── globals.css
```
---
## 4. Fichiers de Configuration
### next.config.ts
```typescript
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
images: {
remotePatterns: [
{
protocol: "https",
hostname: "**.example.com",
},
],
},
};
export default nextConfig;
```
### tsconfig.json
```json
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"checkJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"~/*": ["./src/*"]
}
},
"include": [
".eslintrc.cjs",
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": ["node_modules"]
}
```
### tailwind.config.ts
```typescript
import type { Config } from "tailwindcss";
const config: Config = {
darkMode: ["class"],
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
container: {
center: true,
padding: "2rem",
screens: {
"2xl": "1400px",
},
},
extend: {
colors: {
border: "hsl(var(--border))",
input: "hsl(var(--input))",
ring: "hsl(var(--ring))",
background: "hsl(var(--background))",
foreground: "hsl(var(--foreground))",
primary: {
DEFAULT: "hsl(var(--primary))",
foreground: "hsl(var(--primary-foreground))",
},
secondary: {
DEFAULT: "hsl(var(--secondary))",
foreground: "hsl(var(--secondary-foreground))",
},
destructive: {
DEFAULT: "hsl(var(--destructive))",
foreground: "hsl(var(--destructive-foreground))",
},
muted: {
DEFAULT: "hsl(var(--muted))",
foreground: "hsl(var(--muted-foreground))",
},
accent: {
DEFAULT: "hsl(var(--accent))",
foreground: "hsl(var(--accent-foreground))",
},
popover: {
DEFAULT: "hsl(var(--popover))",
foreground: "hsl(var(--popover-foreground))",
},
card: {
DEFAULT: "hsl(var(--card))",
foreground: "hsl(var(--card-foreground))",
},
},
borderRadius: {
lg: "var(--radius)",
md: "calc(var(--radius) - 2px)",
sm: "calc(var(--radius) - 4px)",
},
},
},
plugins: [require("tailwindcss-animate")],
};
export default config;
```
### .prettierrc
```json
{
"semi": false,
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "es5",
"printWidth": 100,
"plugins": ["prettier-plugin-tailwindcss"]
}
```
---
## 5. Fichiers de Base
### src/lib/utils.ts
```typescript
import { type ClassValue, clsx } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
```
### src/stores/uiStore.ts
```typescript
import { create } from "zustand";
import { devtools } from "zustand/middleware";
interface UIState {
isSidebarOpen: boolean;
isModalOpen: boolean;
modalContent: React.ReactNode | null;
theme: "light" | "dark" | "system";
toggleSidebar: () => void;
openModal: (content: React.ReactNode) => void;
closeModal: () => void;
setTheme: (theme: UIState["theme"]) => void;
}
export const useUIStore = create<UIState>()(
devtools(
(set) => ({
isSidebarOpen: true,
isModalOpen: false,
modalContent: null,
theme: "system",
toggleSidebar: () =>
set((state) => ({ isSidebarOpen: !state.isSidebarOpen })),
openModal: (content) =>
set({ isModalOpen: true, modalContent: content }),
closeModal: () =>
set({ isModalOpen: false, modalContent: null }),
setTheme: (theme) => set({ theme }),
}),
{ name: "UIStore" }
)
);
```
### src/app/layout.tsx
```typescript
import "~/styles/globals.css";
import { type Metadata } from "next";
import { Geist } from "next/font/google";
import { TRPCReactProvider } from "~/trpc/react";
import { NuqsAdapter } from "nuqs/adapters/next/app";
export const metadata: Metadata = {
title: "Mon App",
description: "Description de mon application",
};
const geist = Geist({
subsets: ["latin"],
variable: "--font-geist-sans",
});
export default function RootLayout({
children,
}: Readonly<{ children: React.ReactNode }>) {
return (
<html lang="fr" className={geist.variable}>
<body>
<TRPCReactProvider>
<NuqsAdapter>{children}</NuqsAdapter>
</TRPCReactProvider>
</body>
</html>
);
}
```
---
## 6. Prisma Schema
### Configuration
```prisma
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
output = "../generated/prisma"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
// Models métier
model Post {
id String @id @default(cuid())
name String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
createdBy User @relation(fields: [createdById], references: [id])
createdById String
@@index([name])
}
// Models Better Auth
model User {
id String @id
name String
email String
emailVerified Boolean @default(false)
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
sessions Session[]
accounts Account[]
posts Post[]
@@unique([email])
@@map("user")
}
model Session {
id String @id
expiresAt DateTime
token String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
ipAddress String?
userAgent String?
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([token])
@@map("session")
}
model Account {
id String @id
accountId String
providerId String
userId String
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
accessToken String?
refreshToken String?
idToken String?
accessTokenExpiresAt DateTime?
refreshTokenExpiresAt DateTime?
scope String?
password String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("account")
}
model Verification {
id String @id
identifier String
value String
expiresAt DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("verification")
}
```
---
## 7. Variables d'Environnement
### .env.example
```env
# Database
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
# Auth (Better Auth)
BETTER_AUTH_SECRET="your-secret-key-min-32-characters-long"
BETTER_AUTH_GITHUB_CLIENT_ID=""
BETTER_AUTH_GITHUB_CLIENT_SECRET=""
# App
NODE_ENV="development"
```
### src/env.js
```typescript
import { createEnv } from "@t3-oss/env-nextjs";
import { z } from "zod";
export const env = createEnv({
server: {
BETTER_AUTH_SECRET:
process.env.NODE_ENV === "production"
? z.string()
: z.string().optional(),
BETTER_AUTH_GITHUB_CLIENT_ID: z.string(),
BETTER_AUTH_GITHUB_CLIENT_SECRET: z.string(),
DATABASE_URL: z.string().url(),
NODE_ENV: z
.enum(["development", "test", "production"])
.default("development"),
},
client: {
// NEXT_PUBLIC_CLIENTVAR: z.string(),
},
runtimeEnv: {
BETTER_AUTH_SECRET: process.env.BETTER_AUTH_SECRET,
BETTER_AUTH_GITHUB_CLIENT_ID: process.env.BETTER_AUTH_GITHUB_CLIENT_ID,
BETTER_AUTH_GITHUB_CLIENT_SECRET:
process.env.BETTER_AUTH_GITHUB_CLIENT_SECRET,
DATABASE_URL: process.env.DATABASE_URL,
NODE_ENV: process.env.NODE_ENV,
},
skipValidation: !!process.env.SKIP_ENV_VALIDATION,
emptyStringAsUndefined: true,
});
```
---
## 8. Scripts Package.json
```json
{
"scripts": {
"build": "next build",
"check": "next lint && tsc --noEmit",
"db:generate": "prisma migrate dev",
"db:migrate": "prisma migrate deploy",
"db:push": "prisma db push",
"db:studio": "prisma studio",
"dev": "next dev --turbo",
"format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,mdx}\" --cache",
"format:write": "prettier --write \"**/*.{ts,tsx,js,jsx,mdx}\" --cache",
"postinstall": "prisma generate",
"lint": "next lint",
"lint:fix": "next lint --fix",
"preview": "next build && next start",
"start": "next start",
"typecheck": "tsc --noEmit"
}
}
```
---
## 9. Commandes de Démarrage
### Initialisation Base de Données
```bash
# Générer le client Prisma
npm run db:generate
# Créer les tables (développement)
npm run db:push
# Ou créer une migration
npm run db:migrate
```
### Lancer le Projet
```bash
# Développement
npm run dev
# Production
npm run build
npm start
```
### Vérifications
```bash
# Type checking
npm run typecheck
# Linting
npm run lint
# Format
npm run format:check
```
---
## 10. Checklist Initialisation
### Configuration
- [ ] Projet T3 créé
- [ ] Tailwind CSS configuré
- [ ] shadcn/ui initialisé
- [ ] ESLint + Prettier configurés
- [ ] Path aliases `~/` configurés
### Dépendances
- [ ] Zustand installé
- [ ] Nuqs installé
- [ ] React Hook Form + Zod installés
- [ ] next-intl installé
- [ ] date-fns installé
- [ ] Better Auth configuré
- [ ] Prisma installé et configuré
- [ ] Composants shadcn/ui essentiels ajoutés
### Structure
- [ ] Dossiers créés selon la structure
- [ ] Fichiers de base créés
- [ ] Providers configurés dans `layout.tsx`
### Base de Données
- [ ] Schema Prisma créé
- [ ] Models Better Auth ajoutés
- [ ] Variables d'environnement configurées
- [ ] Client Prisma généré
- [ ] Tables créées
### Git
- [ ] .gitignore configuré
- [ ] Premier commit effectué
---
**Règle d'or** : Utiliser `create-t3-app` pour initialiser le projet, puis ajouter les dépendances additionnelles (shadcn/ui, Zustand, Nuqs) selon les besoins.
In this repository6 other files it gives its agents.
- .cursor/rules/backend_development.mdcCursor rule
- .cursor/rules/components/basic_component.mdcCursor rule
- .cursor/rules/components/ui_components_reference.mdcCursor rule
- .cursor/rules/components_usage_policy.mdcCursor rule
- .cursor/rules/frontend_development.mdcCursor rule
- .cursor/rules/README.mdCursor rule
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

