Pages
In Chidori, pages are the building blocks of your application. Based on Rasengan.js's file-based routing system, creating pages is as simple as creating files in the _routes directory.
Creating Pages
Basic Page Structure
Pages in Chidori can be created using two main file types:
.page.mdx- For pages with Markdown content and React components.page.tsx- For pure React components
MDX Pages
MDX pages are perfect for documentation, blog posts, or content-heavy pages:
--- metadata: title: About Us description: Learn more about our company toc: true --- # About Us Welcome to our about page! Here you can use **Markdown** syntax and also embed React components. <Note title="info"> This is a custom React component embedded in MDX! </Note> ## Our Mission We're building amazing things with Chidori and Rasengan.js.
React Component Pages
For interactive pages or complex UI, use .page.tsx:
import React from 'react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; export default function Dashboard() { return ( <div className="container mx-auto p-6"> <h1 className="text-3xl font-bold mb-6">Dashboard</h1> <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"> <Card> <CardHeader> <CardTitle>Analytics</CardTitle> </CardHeader> <CardContent> <p>View your application analytics here.</p> </CardContent> </Card> <Card> <CardHeader> <CardTitle>Users</CardTitle> </CardHeader> <CardContent> <p>Manage your application users.</p> </CardContent> </Card> </div> </div> ); }
Read more about File-based routing from the documentation of Rasengan.js
Page Metadata
Pages can include metadata in the frontmatter:
--- metadata: title: Page Title description: Page description for SEO openGraph: title: Chidori description: Chidori Documentation image: https://example.com/image.jpg url: https://example.com twitter: title: Chidori description: Chidori Documentation image: https://example.com/image.jpg card: summary_large_image ---
Read more about Metadata from the documentation of Rasengan.js
Route Structure
The file structure in _routes directly maps to your URL structure:
_routes/ ├── index.page.mdx # → / ├── about.page.mdx # → /about ├── contact.page.tsx # → /contact ├── docs/ │ ├── index.page.mdx # → /docs │ ├── getting-started.page.mdx # → /docs/getting-started │ └── advanced/ │ ├── index.page.mdx # → /docs/advanced │ └── api.page.mdx # → /docs/advanced/api └── blog/ ├── [slug].page.mdx # → /blog/:slug (dynamic) └── [category]/ └── [slug].page.mdx # → /blog/:category/:slug
Dynamic Routes
Create dynamic routes using square brackets [param]:
import React from 'react'; import { useParams } from 'rasengan'; export default function BlogPost() { const { slug } = useParams<{ slug: string }>(); return ( <div className="container mx-auto p-6"> <h1 className="text-3xl font-bold mb-6"> Blog Post: {slug} </h1> <p>This is a dynamic blog post page.</p> </div> ); }
Page Components
Using Custom Components
You can use any React component in your pages:
import React from 'react'; import { Hero } from '@/components/Hero'; import { Features } from '@/components/Features'; import { Testimonials } from '@/components/Testimonials'; export default function Home() { return ( <> <Hero /> <Features /> <Testimonials /> </> ); }
Page Layouts
Pages automatically inherit layouts from parent directories. You can also create page-specific layouts:
import React from 'react'; import { DashboardLayout } from '@/layouts/DashboardLayout'; import { DashboardContent } from '@/components/DashboardContent'; export default function Dashboard() { return ( <DashboardLayout> <DashboardContent /> </DashboardLayout> ); }
Learn how to create layouts at the directory level.
Data Fetching
Static Data
For static data, import directly or use top-level imports:
import React from 'react'; import { products } from '@/data/products'; export default function Products() { return ( <div className="container mx-auto p-6"> <h1 className="text-3xl font-bold mb-6">Products</h1> <div className="grid grid-cols-1 md:grid-cols-3 gap-6"> {products.map((product) => ( <div key={product.id} className="border rounded-lg p-4"> <h3>{product.name}</h3> <p>{product.description}</p> </div> ))} </div> </div> ); }
Client-Side Data Fetching
For dynamic data, use React hooks:
import React, { useState, useEffect } from 'react'; interface Post { id: number; title: string; body: string; } export default function Posts() { const [posts, setPosts] = useState<Post[]>([]); const [loading, setLoading] = useState(true); useEffect(() => { fetch('https://jsonplaceholder.typicode.com/posts') .then((response) => response.json()) .then((data) => { setPosts(data); setLoading(false); }); }, []); if (loading) return <div>Loading...</div>; return ( <div className="container mx-auto p-6"> <h1 className="text-3xl font-bold mb-6">Posts</h1> <div className="space-y-4"> {posts.map((post) => ( <div key={post.id} className="border rounded-lg p-4"> <h3 className="font-bold">{post.title}</h3> <p>{post.body}</p> </div> ))} </div> </div> ); }
Page Styling
Using CSS Modules
import React from 'react'; import styles from './about.module.css'; export default function About() { return ( <div className={styles.container}> <h1 className={styles.title}>About Us</h1> <p className={styles.description}> Learn more about our amazing company. </p> </div> ); }
Using Tailwind CSS
import React from 'react'; export default function Contact() { return ( <div className="min-h-screen bg-gray-50 py-12 px-4"> <div className="max-w-2xl mx-auto"> <h1 className="text-4xl font-bold text-center mb-8"> Contact Us </h1> <form className="space-y-6"> <div> <label className="block text-sm font-medium mb-2"> Name </label> <input type="text" className="w-full px-4 py-2 border rounded-lg focus:ring-2 focus:ring-blue-500" /> </div> </form> </div> </div> ); }
Best Practices
1. File Naming
- Use kebab-case for file names:
getting-started.page.mdx - Keep names descriptive and concise
- Use index files for directory roots
2. Component Organization
- Keep page components focused on layout and composition
- Extract reusable logic into custom hooks
- Use shared components for common UI patterns
3. Performance
- Use lazy loading for heavy components
- Optimize images and assets
- Implement proper error boundaries
4. SEO
- Always include descriptive metadata
- Use semantic HTML elements
- Implement proper heading hierarchy
Advanced Features
Route Guards
Protect pages with authentication:
import React from 'react'; import { useAuth } from '@/hooks/useAuth'; import { useNavigate } from 'rasengan'; export default function AdminDashboard() { const { user } = useAuth(); const navigate = useNavigate(); React.useEffect(() => { if (!user || !user.isAdmin) { navigate('/login'); } }, [user, router]); if (!user || !user.isAdmin) { return <div>Redirecting...</div>; } return ( <div className="container mx-auto p-6"> <h1 className="text-3xl font-bold mb-6">Admin Dashboard</h1> {/* Admin content */} </div> ); }
Page Transitions
Add smooth transitions between pages:
import React from 'react'; import { AnimatePresence, motion } from 'framer-motion'; import { useLocation } from 'rasengan'; export default function Layout({ children }: { children: React.ReactNode }) { const location = useLocation(); return ( <AnimatePresence mode="wait"> <motion.div key={location.pathname} initial={{ opacity: 0, y: 20 }} animate={{ opacity: 1, y: 0 }} exit={{ opacity: 0, y: -20 }} transition={{ duration: 0.3 }} > {children} </motion.div> </AnimatePresence> ); }
