The web app follows a clean, feature-based architecture with clear separation of concerns.
Directory Overview
web/
├── app/ # Next.js App Router
│ ├── layout.tsx # Root layout with providers
│ ├── page.tsx # Home page route
│ ├── globals.css # Global styles & design tokens
│ └── favicon.ico # App favicon
├── components/ # Reusable components
│ ├── ui/ # shadcn/ui components
│ ├── blocks/ # Composite components
│ └── lib/ # Utilities
├── features/ # Feature modules
│ └── home/ # Home feature
├── public/ # Static assets
├── package.json # Dependencies & scripts
├── tsconfig.json # TypeScript config
├── components.json # shadcn/ui config
└── next.config.ts # Next.js config
Core Directories
app/ - App Router
Next.js App Router with file-based routing.
app/layout.tsx
app/page.tsx
import { ThemeProvider } from "@/components/blocks/theme/theme-provider" ;
import type { Metadata } from "next" ;
import { Inter } from "next/font/google" ;
import "./globals.css" ;
const inter = Inter ({ subsets: [ "latin" ], variable: "--font-sans" });
export const metadata : Metadata = {
title: "Agent Toolkit TS" ,
description: "An opinionated starter template for building with GitHub Copilot" ,
};
export default function RootLayout ({
children ,
} : Readonly <{
children : React . ReactNode ;
}>) {
return (
< html lang = "en" >
< body className = { ` ${ inter . variable } antialiased` } >
< ThemeProvider
attribute = "class"
defaultTheme = "system"
enableSystem
disableTransitionOnChange
>
{ children }
</ ThemeProvider >
</ body >
</ html >
);
}
Key Pattern : Route handlers (page.tsx) are thin wrappers that delegate to feature components.
components/ - Reusable Components
Three-tier component organization:
Primitive UI components from shadcn/ui
Built on Base UI primitives
Styled with Tailwind CSS
Fully accessible (ARIA)
Typed with TypeScript
Examples:
button.tsx - Button component with variants
card.tsx - Card layout components
heading.tsx - Heading with semantic levels
dropdown-menu.tsx - Accessible menus
Composite components that combine UI primitives
Theme components (provider, toggle)
Navigation blocks
Layout blocks
Examples:
theme/theme-provider.tsx - Dark mode provider
theme/theme-toggle.tsx - Theme switcher
Utilities and helpers
utils.ts - The cn() utility for Tailwind classes
Type definitions
Constants
features/ - Feature Modules
Feature-based organization for scalability:
features/
└── home/
└── home-page.tsx
Each feature can contain:
Page components
Feature-specific components
Hooks
State management (Zustand stores)
API utilities
Types
As your app grows, add features like features/auth/, features/dashboard/, etc. Each feature is self-contained.
File Naming Conventions
Type Convention Example Components kebab-case.tsxtheme-toggle.tsxPages page.tsxapp/about/page.tsxLayouts layout.tsxapp/layout.tsxUtilities kebab-case.tsutils.tsTypes kebab-case.tsuser-types.tsHooks use-*.tsuse-theme.ts
Do not mix naming conventions. Stick to kebab-case for all files except Next.js special files (page.tsx, layout.tsx).
Import Aliases
Use the @/ alias for clean imports:
// ✅ Good - with alias
import { Button } from "@/components/ui/button" ;
import { HomePage } from "@/features/home/home-page" ;
// ❌ Bad - relative paths
import { Button } from "../../../components/ui/button" ;
import { HomePage } from "./features/home/home-page" ;
Configured in tsconfig.json:
{
"compilerOptions" : {
"paths" : {
"@/*" : [ "./*" ]
}
}
}
Adding New Features
Create feature directory
mkdir -p features/dashboard
Add feature components
touch features/dashboard/dashboard-page.tsx
touch features/dashboard/dashboard-stats.tsx
Create route in app/
mkdir -p app/dashboard
touch app/dashboard/page.tsx
Wire up the route
import { DashboardPage } from "@/features/dashboard/dashboard-page" ;
export default function Dashboard () {
return < DashboardPage /> ;
}
Static Assets
public/ Directory
Static files served from the root:
public/
└── copilot-cli.svg
Reference in components:
import Image from "next/image" ;
< Image src = "/copilot-cli.svg" alt = "Logo" width = { 60 } height = { 60 } />
Files in public/ are served at the root path. /copilot-cli.svg maps to public/copilot-cli.svg
Design Tokens
Global styles and CSS variables live in app/globals.css:
:root {
--background : light-dark( oklch ( 1 0 0 ), oklch ( 0.145 0 0 ));
--foreground : light-dark( oklch ( 0.145 0 0 ), oklch ( 0.985 0 0 ));
--primary : light-dark( oklch ( 0.6 0.13 163 ), oklch ( 0.7 0.15 162 ));
--radius : 0.625 rem ;
/* ... more tokens */
}
Modern CSS light-dark() function automatically switches colors based on color-scheme. Works with the ThemeProvider.
Best Practices
Co-location Keep related files together in feature directories
Import Aliases Always use @/ prefix for absolute imports
Thin Routes Keep app/ routes minimal, delegate to features
Single Source UI components only in components/ui/
Next Steps
Components Explore the component patterns
Features Learn feature-based architecture