386 lines
9.5 KiB
Markdown
386 lines
9.5 KiB
Markdown
# TanStack Router + Tailwind 4 Setup Complete ✅
|
|
|
|
## What's Been Configured
|
|
|
|
### 1. TanStack Router with File-Based Routing
|
|
- **Routes directory**: `frontend/src/routes/`
|
|
- **Root route**: `root.tsx` - Layout with header, footer, and outlet
|
|
- **Home route**: `index.tsx` - Welcome page with getting started cards
|
|
- **Articles route**: `articles.tsx` - Article list with card grid
|
|
- **Router configuration**: Uses `createRouter` with route tree
|
|
|
|
### 2. Tailwind CSS 4 (CSS-First)
|
|
- **Configuration**: In `src/styles.css` using `@import` and `@theme` directives
|
|
- **Vite integration**: `tailwindcss()` plugin in `vite.config.ts`
|
|
- **No config file needed**: Tailwind 4 CSS-first doesn't use `tailwind.config.js`
|
|
- **Shadcn/ui compatible**: CSS variables for theming
|
|
|
|
### 3. Routing Structure
|
|
```
|
|
frontend/src/routes/
|
|
├── root.tsx # Main layout (header, footer, <Outlet />)
|
|
├── index.tsx # Home page (/)
|
|
└── articles.tsx # Articles list (/articles)
|
|
```
|
|
|
|
### 4. Page Templates
|
|
|
|
#### Root Layout (`root.tsx`)
|
|
- Header with navigation (Home, Articles)
|
|
- Main content area with `<Outlet />`
|
|
- Footer with copyright
|
|
- Meta tags for SEO
|
|
- CSS import for Tailwind
|
|
|
|
#### Home Page (`index.tsx`)
|
|
- Welcome cards with:
|
|
- "Welcome" - Introduction
|
|
- "Get Started" - Feature list
|
|
- Centered layout with max-width container
|
|
|
|
#### Articles Page (`articles.tsx`)
|
|
- Grid of article cards (responsive: 1/2/3 columns)
|
|
- Loading state
|
|
- Error state
|
|
- Empty state (no articles)
|
|
- Each card shows:
|
|
- Featured image (if available)
|
|
- Title (truncated to 2 lines)
|
|
- Excerpt (truncated to 3 lines)
|
|
- Content preview (truncated to 4 lines)
|
|
- Date and view count
|
|
|
|
## How It Works
|
|
|
|
### 1. Route Configuration
|
|
```tsx
|
|
// src/routes/root.tsx
|
|
import { createRootRoute, Link, Outlet } from '@tanstack/react-router'
|
|
|
|
export const rootRoute = createRootRoute({
|
|
head: () => ({
|
|
meta: [
|
|
{
|
|
title: 'Placebo.mk - Sarcastic News from Macedonia',
|
|
description: 'Latest news and articles from Macedonia with a sarcastic twist',
|
|
},
|
|
],
|
|
links: [{ rel: 'stylesheet', href: '../../styles.css' }],
|
|
}),
|
|
component: () => (
|
|
<div>
|
|
<header>...</header>
|
|
<main><Outlet /></main>
|
|
<footer>...</footer>
|
|
</div>
|
|
),
|
|
})
|
|
```
|
|
|
|
### 2. Route Tree
|
|
```tsx
|
|
// src/main.tsx
|
|
import { createRouter } from '@tanstack/react-router'
|
|
import { rootRoute } from './routes/root'
|
|
import { indexRoute } from './routes/index'
|
|
import { articlesRoute } from './routes/articles'
|
|
|
|
const routeTree = rootRoute.addChildren([indexRoute, articlesRoute])
|
|
const router = createRouter({ routeTree, context: { queryClient } })
|
|
```
|
|
|
|
### 3. File-Based Routes
|
|
```tsx
|
|
// src/routes/index.tsx
|
|
import { createFileRoute } from '@tanstack/react-router'
|
|
|
|
export const Route = createFileRoute('/')({
|
|
component: () => <div>Home</div>,
|
|
})
|
|
```
|
|
|
|
```tsx
|
|
// src/routes/articles.tsx
|
|
import { createFileRoute } from '@tanstack/react-router'
|
|
|
|
export const Route = createFileRoute('/articles')({
|
|
component: () => <div>Articles</div>,
|
|
})
|
|
```
|
|
|
|
### 4. Tailwind 4 CSS-First Setup
|
|
|
|
#### In `src/styles.css`:
|
|
```css
|
|
@import "tailwindcss";
|
|
|
|
@theme {
|
|
--color-primary: oklch(0.647 0.22 0.23);
|
|
--font-sans: "Inter", sans-serif;
|
|
}
|
|
```
|
|
|
|
#### In `vite.config.ts`:
|
|
```ts
|
|
import { defineConfig } from 'vite'
|
|
import react from '@vitejs/plugin-react'
|
|
import tailwindcss from 'tailwindcss'
|
|
import path from 'path'
|
|
|
|
export default defineConfig({
|
|
plugins: [react(), tailwindcss()],
|
|
resolve: {
|
|
alias: {
|
|
'@': path.resolve(__dirname, './src'),
|
|
},
|
|
},
|
|
})
|
|
```
|
|
|
|
## Running the App
|
|
|
|
```bash
|
|
cd frontend
|
|
npm run dev
|
|
```
|
|
|
|
Visit:
|
|
- Home: http://localhost:5173/
|
|
- Articles: http://localhost:5173/articles
|
|
|
|
## Adding New Routes
|
|
|
|
### Method 1: File-Based Routes (Recommended)
|
|
Create a new file in `src/routes/`:
|
|
|
|
```tsx
|
|
// src/routes/article-detail.tsx
|
|
import { createFileRoute } from '@tanstack/react-router'
|
|
import { useQuery } from '@tanstack/react-query'
|
|
import * as api from '../lib/api'
|
|
|
|
export const Route = createFileRoute('/articles/$id')({
|
|
component: () => {
|
|
const { id } = Route.useParams()
|
|
const { data } = useQuery({
|
|
queryKey: ['article', id],
|
|
queryFn: () => api.fetchArticleById(id),
|
|
})
|
|
|
|
return (
|
|
<div>
|
|
<h1>{data?.title}</h1>
|
|
<div dangerouslySetInnerHTML={{ __html: data?.content || '' }} />
|
|
</div>
|
|
)
|
|
},
|
|
})
|
|
```
|
|
|
|
Then add to `src/main.tsx`:
|
|
```tsx
|
|
import { articleDetailRoute } from './routes/article-detail'
|
|
|
|
const routeTree = rootRoute.addChildren([indexRoute, articlesRoute, articleDetailRoute])
|
|
```
|
|
|
|
### Method 2: Using Link for Navigation
|
|
```tsx
|
|
import { Link } from '@tanstack/react-router'
|
|
|
|
// In your component
|
|
<Link to="/article/123">Read Article</Link>
|
|
```
|
|
|
|
## Adding Shadcn/ui Components
|
|
|
|
### Using the CLI (Recommended)
|
|
```bash
|
|
cd frontend
|
|
npx shadcn@latest add [component-name]
|
|
```
|
|
|
|
### Common Components to Add
|
|
```bash
|
|
npx shadcn@latest add button # (already installed)
|
|
npx shadcn@latest add badge # For article tags
|
|
npx shadcn@latest add input # For search
|
|
npx shadcn@latest add dialog # For article details
|
|
npx shadcn@latest add dropdown-menu
|
|
npx shadcn@latest add select
|
|
npx shadcn@latest add tabs # For content organization
|
|
npx shadcn@latest add separator # For visual separation
|
|
npx shadcn@latest add skeleton # For loading states
|
|
npx shadcn@latest add pagination # For article list navigation
|
|
```
|
|
|
|
### Using Components in Routes
|
|
```tsx
|
|
import { Button } from '@/components/ui/button'
|
|
|
|
export const Route = createFileRoute('/articles')({
|
|
component: () => (
|
|
<div>
|
|
<Button variant="default">Click me</Button>
|
|
</div>
|
|
),
|
|
})
|
|
```
|
|
|
|
## Customization
|
|
|
|
### Theme Colors
|
|
|
|
Edit `src/styles.css`:
|
|
|
|
```css
|
|
@theme {
|
|
--color-primary: oklch(0.647 0.22 0.23);
|
|
--color-secondary: oklch(0.8 0.15 0.1);
|
|
--font-sans: "Your Font", sans-serif;
|
|
}
|
|
```
|
|
|
|
### Adding Fonts
|
|
|
|
Edit `src/styles.css`:
|
|
|
|
```css
|
|
@theme {
|
|
--font-sans: "Inter", sans-serif;
|
|
}
|
|
|
|
@layer base {
|
|
* {
|
|
@apply font-sans;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Dark Mode
|
|
|
|
Add dark mode toggle to header:
|
|
|
|
```tsx
|
|
import { useEffect } from 'react'
|
|
|
|
export const rootRoute = createRootRoute({
|
|
component: () => {
|
|
const toggleDarkMode = () => {
|
|
document.documentElement.classList.toggle('dark')
|
|
localStorage.setItem('theme',
|
|
document.documentElement.classList.contains('dark') ? 'dark' : 'light'
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div>
|
|
<header>
|
|
<button onClick={toggleDarkMode}>
|
|
Toggle Theme
|
|
</button>
|
|
</header>
|
|
<main><Outlet /></main>
|
|
</div>
|
|
)
|
|
},
|
|
})
|
|
```
|
|
|
|
## File Structure
|
|
|
|
```
|
|
frontend/
|
|
├── src/
|
|
│ ├── components/
|
|
│ │ └── ui/ # shadcn/ui components
|
|
│ ├── lib/
|
|
│ │ ├── api.ts # Backend API functions
|
|
│ │ ├── query-client.ts # TanStack Query configuration
|
|
│ │ └── utils.ts # cn() utility (for shadcn/ui)
|
|
│ ├── routes/ # TanStack Router routes
|
|
│ │ ├── root.tsx # Root layout
|
|
│ │ ├── index.tsx # Home page
|
|
│ │ └── articles.tsx # Articles list
|
|
│ ├── styles.css # Tailwind CSS + theme
|
|
│ └── main.tsx # App entry point
|
|
├── components.json # shadcn/ui configuration
|
|
├── vite.config.ts # Vite + Tailwind CSS plugin
|
|
├── tailwind.config.js # (Not used - CSS-first approach)
|
|
└── package.json
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Routes Not Working
|
|
|
|
1. Check that route files are in `src/routes/`
|
|
2. Check export: `export const Route = createFileRoute(...)`
|
|
3. Check imports in `src/main.tsx`
|
|
4. Restart dev server
|
|
|
|
### Styles Not Applying
|
|
|
|
1. Check `src/styles.css` has `@import "tailwindcss"`
|
|
2. Check `vite.config.ts` has `tailwindcss()` plugin
|
|
3. Restart dev server
|
|
4. Hard refresh browser (Ctrl+Shift+R)
|
|
|
|
### Component Imports Not Working
|
|
|
|
1. Check `tsconfig.json` has `@/` paths configured
|
|
2. Check `vite.config.ts` has resolve alias for `@/`
|
|
3. Restart TypeScript server (VSCode: Cmd+Shift+P → "TypeScript: Restart TS Server")
|
|
|
|
### TanStack Query Not Fetching
|
|
|
|
1. Check backend is running: `http://localhost:3000`
|
|
2. Check `.env` has correct API URL
|
|
3. Check browser network tab for failed requests
|
|
4. Check CORS configuration in backend
|
|
|
|
## Best Practices
|
|
|
|
### 1. Route Organization
|
|
- Keep routes in `src/routes/` directory
|
|
- Use file-based routing for simple pages
|
|
- Use nested routes for related content
|
|
|
|
### 2. Component Reusability
|
|
- Create reusable components in `src/components/`
|
|
- Use shadcn/ui for consistent styling
|
|
- Extract common patterns (loading states, error states)
|
|
|
|
### 3. Data Fetching
|
|
- Use TanStack Query for all server state
|
|
- Implement proper loading and error states
|
|
- Use query keys consistently
|
|
|
|
### 4. Performance
|
|
- Use `lazy()` for code splitting large components
|
|
- Implement image lazy loading
|
|
- Use `defer` for non-critical resources
|
|
|
|
### 5. SEO
|
|
- Add meta tags to root route
|
|
- Add Open Graph tags
|
|
- Implement structured data
|
|
|
|
## Next Steps
|
|
|
|
1. **Restart dev server** to load new configuration
|
|
2. **Visit home page**: http://localhost:5173/
|
|
3. **Visit articles page**: http://localhost:5173/articles
|
|
4. **Add article detail page** with `$id` parameter
|
|
5. **Add shadcn/ui components** as needed
|
|
6. **Add dark mode toggle**
|
|
7. **Add search functionality**
|
|
8. **Add category/tag filtering**
|
|
|
|
## Resources
|
|
|
|
- [TanStack Router Docs](https://tanstack.com/router/latest/docs/framework/react/start/file-based-routing)
|
|
- [Tailwind CSS 4 Docs](https://tailwindcss.com/docs/installation/using-postcss)
|
|
- [Shadcn/ui Documentation](https://ui.shadcn.com)
|
|
- [TanStack Query Docs](https://tanstack.com/query/latest)
|