Skip to main content

Production React Templates - Complete Page Layouts

Full JSX implementations with TypeScript, routing, and data integration

DashboardGrid Template

Dashboard.jsx

import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import './Dashboard.css';

const Dashboard = ({ user, onCreateProject }) => {
const [stats, setStats] = useState({
activeProjects: 0,
tasksCompleted: 0,
teamMembers: 0,
avgCompletionTime: '0 days',
});

const [projects, setProjects] = useState([]);
const [activities, setActivities] = useState([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
// Simulate API call
const fetchDashboardData = async () => {
try {
setLoading(true);

// In production, replace with actual API calls
await new Promise(resolve => setTimeout(resolve, 1000));

setStats({
activeProjects: 12,
tasksCompleted: 148,
teamMembers: 24,
avgCompletionTime: '3.2 days',
});

setProjects([
{
id: 1,
title: 'Website Redesign',
progress: 75,
dueIn: 5,
team: [
{ id: 1, avatar: '/alice.jpg', name: 'Alice' },
{ id: 2, avatar: '/bob.jpg', name: 'Bob' },
],
teamCount: 5,
},
{
id: 2,
title: 'Mobile App Development',
progress: 45,
dueIn: 12,
team: [
{ id: 3, avatar: '/charlie.jpg', name: 'Charlie' },
],
teamCount: 3,
},
{
id: 3,
title: 'API Integration',
progress: 90,
dueIn: 2,
team: [
{ id: 1, avatar: '/alice.jpg', name: 'Alice' },
],
teamCount: 2,
},
]);

setActivities([
{
id: 1,
user: { name: 'Alice Johnson', avatar: '/alice.jpg' },
action: 'completed task "Update homepage"',
time: '2 hours ago',
},
{
id: 2,
user: { name: 'Bob Smith', avatar: '/bob.jpg' },
action: 'created new project "Marketing Campaign"',
time: '5 hours ago',
},
]);
} catch (error) {
console.error('Failed to fetch dashboard data:', error);
} finally {
setLoading(false);
}
};

fetchDashboardData();
}, []);

if (loading) {
return (
<div className="dashboard-layout">
<div className="dashboard-main">
<div className="loading-skeleton">
<div className="skeleton skeleton--title"></div>
<div className="skeleton skeleton--stats"></div>
<div className="skeleton skeleton--grid"></div>
</div>
</div>
</div>
);
}

return (
<div className="dashboard-layout">
<main className="dashboard-main">
{/* Page Header */}
<div className="page-header">
<div className="page-header__content">
<h1 className="page-header__title">
Welcome back, {user?.firstName || 'User'}
</h1>
<p className="page-header__description">
Overview of your projects and team activity
</p>
</div>
<div className="page-header__actions">
<button
className="button button--primary"
onClick={onCreateProject}
>
<svg className="button__icon" width="16" height="16" viewBox="0 0 16 16">
<path
d="M8 4v8M4 8h8"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
/>
</svg>
New Project
</button>
</div>
</div>

{/* Stats Grid */}
<div className="stats-grid">
<StatCard
label="Active Projects"
value={stats.activeProjects}
change="+2 from last month"
positive
/>
<StatCard
label="Tasks Completed"
value={stats.tasksCompleted}
change="+23%"
positive
/>
<StatCard
label="Team Members"
value={stats.teamMembers}
change="No change"
/>
<StatCard
label="Avg Completion Time"
value={stats.avgCompletionTime}
change="+0.4 days"
negative
/>
</div>

{/* Dashboard Grid */}
<div className="dashboard-grid">
{/* Active Projects Card */}
<article className="card">
<div className="card__header">
<h2 className="card__title">Active Projects</h2>
<a href="/projects" className="card__action">
View all →
</a>
</div>
<div className="card__content">
<div className="project-list">
{projects.map((project) => (
<ProjectItem key={project.id} project={project} />
))}
</div>
</div>
</article>

{/* Recent Activity Card */}
<article className="card">
<div className="card__header">
<h2 className="card__title">Recent Activity</h2>
</div>
<div className="card__content">
<div className="activity-list">
{activities.map((activity) => (
<ActivityItem key={activity.id} activity={activity} />
))}
</div>
</div>
</article>

{/* Quick Actions Card */}
<article className="card">
<div className="card__header">
<h2 className="card__title">Quick Actions</h2>
</div>
<div className="card__content">
<div className="action-list">
<button className="action-list__item" onClick={onCreateProject}>
<svg className="action-list__icon" width="20" height="20">
<path d="M12 5v14M5 12h14" stroke="currentColor" strokeWidth="2"/>
</svg>
<span>New Project</span>
</button>
<button className="action-list__item">
<svg className="action-list__icon" width="20" height="20">
<path d="M16 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2" stroke="currentColor" strokeWidth="2"/>
</svg>
<span>Invite Team Member</span>
</button>
<button className="action-list__item">
<svg className="action-list__icon" width="20" height="20">
<path d="M9 2h6l4 4v14a2 2 0 01-2 2H7a2 2 0 01-2-2V4a2 2 0 012-2z" stroke="currentColor" strokeWidth="2"/>
</svg>
<span>View Reports</span>
</button>
</div>
</div>
</article>
</div>
</main>
</div>
);
};

// Sub-components

const StatCard = ({ label, value, change, positive, negative }) => (
<div className="stat-card">
<div className="stat-card__label">{label}</div>
<div className="stat-card__value">{value}</div>
{change && (
<div
className={`stat-card__change ${
positive ? 'stat-card__change--positive' : ''
} ${negative ? 'stat-card__change--negative' : ''}`}
>
{change}
</div>
)}
</div>
);

StatCard.propTypes = {
label: PropTypes.string.isRequired,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]).isRequired,
change: PropTypes.string,
positive: PropTypes.bool,
negative: PropTypes.bool,
};

const ProjectItem = ({ project }) => (
<div className="project-item">
<div className="project-item__info">
<h3 className="project-item__title">{project.title}</h3>
<div className="project-item__meta">
<div className="avatar-group">
{project.team.slice(0, 2).map((member) => (
<img
key={member.id}
src={member.avatar}
alt={member.name}
className="avatar avatar--sm avatar--stacked"
/>
))}
{project.teamCount > 2 && (
<div className="avatar avatar--sm avatar--stacked avatar--count">
+{project.teamCount - 2}
</div>
)}
</div>
<span className="project-item__due">Due in {project.dueIn} days</span>
</div>
</div>
<div className="progress-indicator">
<div className="progress-bar">
<div
className="progress-bar__fill"
style={{ width: `${project.progress}%` }}
role="progressbar"
aria-valuenow={project.progress}
aria-valuemin="0"
aria-valuemax="100"
/>
</div>
<span className="progress-indicator__label">{project.progress}%</span>
</div>
</div>
);

ProjectItem.propTypes = {
project: PropTypes.shape({
id: PropTypes.number.isRequired,
title: PropTypes.string.isRequired,
progress: PropTypes.number.isRequired,
dueIn: PropTypes.number.isRequired,
team: PropTypes.arrayOf(PropTypes.object).isRequired,
teamCount: PropTypes.number.isRequired,
}).isRequired,
};

const ActivityItem = ({ activity }) => (
<div className="activity-item">
<img
src={activity.user.avatar}
alt=""
className="avatar avatar--sm"
/>
<div className="activity-item__content">
<p>
<strong>{activity.user.name}</strong> {activity.action}
</p>
<time className="activity-item__time">{activity.time}</time>
</div>
</div>
);

ActivityItem.propTypes = {
activity: PropTypes.shape({
id: PropTypes.number.isRequired,
user: PropTypes.shape({
name: PropTypes.string.isRequired,
avatar: PropTypes.string.isRequired,
}).isRequired,
action: PropTypes.string.isRequired,
time: PropTypes.string.isRequired,
}).isRequired,
};

Dashboard.propTypes = {
user: PropTypes.shape({
firstName: PropTypes.string,
}),
onCreateProject: PropTypes.func.isRequired,
};

export default Dashboard;

KanbanBoard Template

KanbanBoard.jsx

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import './KanbanBoard.css';

const KanbanBoard = ({ initialColumns, initialCards, onCardMove, onCardCreate }) => {
const [columns, setColumns] = useState(initialColumns);
const [cards, setCards] = useState(initialCards);
const [draggedCard, setDraggedCard] = useState(null);

const handleDragStart = (e, cardId) => {
setDraggedCard(cardId);
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/html', e.currentTarget);
};

const handleDragOver = (e) => {
if (e.preventDefault) {
e.preventDefault();
}
e.dataTransfer.dropEffect = 'move';
return false;
};

const handleDrop = (e, columnId) => {
if (e.stopPropagation) {
e.stopPropagation();
}

const card = cards.find(c => c.id === draggedCard);
if (card && card.columnId !== columnId) {
const updatedCards = cards.map(c =>
c.id === draggedCard ? { ...c, columnId } : c
);
setCards(updatedCards);

if (onCardMove) {
onCardMove(draggedCard, columnId);
}
}

setDraggedCard(null);
return false;
};

const getColumnCards = (columnId) => {
return cards.filter(card => card.columnId === columnId);
};

return (
<div className="kanban-layout">
<main className="kanban-main">
{/* Page Header */}
<div className="page-header">
<div className="page-header__content">
<h1 className="page-header__title">Project Tasks</h1>
<div className="tag-list">
<span className="tag">{cards.length} tasks</span>
<span className="tag tag--success">
{cards.filter(c => c.status === 'done').length} completed
</span>
</div>
</div>
<div className="page-header__actions">
<button className="button button--secondary">
<svg width="16" height="16" className="button__icon">
<path d="M3 6h18M9 6V4a2 2 0 012-2h2a2 2 0 012 2v2" stroke="currentColor" strokeWidth="2"/>
</svg>
Filter
</button>
<button
className="button button--primary"
onClick={() => onCardCreate?.('todo')}
>
<svg width="16" height="16" className="button__icon">
<path d="M12 5v14M5 12h14" stroke="currentColor" strokeWidth="2"/>
</svg>
New Task
</button>
</div>
</div>

{/* Kanban Board */}
<div className="kanban-board" role="region" aria-label="Task board">
{columns.map((column) => (
<KanbanColumn
key={column.id}
column={column}
cards={getColumnCards(column.id)}
onDragOver={handleDragOver}
onDrop={handleDrop}
onDragStart={handleDragStart}
onCardCreate={() => onCardCreate?.(column.id)}
/>
))}
</div>
</main>
</div>
);
};

const KanbanColumn = ({
column,
cards,
onDragOver,
onDrop,
onDragStart,
onCardCreate
}) => (
<div
className="kanban-column"
onDragOver={onDragOver}
onDrop={(e) => onDrop(e, column.id)}
>
<div className="kanban-column__header">
<h2 className="kanban-column__title">
<span className={`dot dot--${column.statusColor}`} />
{column.title}
<span className="kanban-column__count">{cards.length}</span>
</h2>
<button
className="kanban-column__add"
onClick={onCardCreate}
aria-label={`Add task to ${column.title}`}
>
<svg width="16" height="16" viewBox="0 0 16 16">
<path d="M8 4v8M4 8h8" stroke="currentColor" strokeWidth="2"/>
</svg>
</button>
</div>

<div className="kanban-column__content">
{cards.map((card) => (
<KanbanCard
key={card.id}
card={card}
onDragStart={onDragStart}
/>
))}
</div>
</div>
);

const KanbanCard = ({ card, onDragStart }) => (
<div
className={`kanban-card ${card.status === 'done' ? 'kanban-card--completed' : ''}`}
draggable
onDragStart={(e) => onDragStart(e, card.id)}
>
<h3 className="kanban-card__title">{card.title}</h3>
{card.description && (
<p className="kanban-card__description">{card.description}</p>
)}

{card.progress !== undefined && (
<div className="kanban-card__progress">
<div className="progress-indicator">
<div className="progress-bar">
<div
className="progress-bar__fill"
style={{ width: `${card.progress}%` }}
/>
</div>
<span className="progress-indicator__label">{card.progress}%</span>
</div>
</div>
)}

<div className="kanban-card__footer">
{card.assignees && card.assignees.length > 0 && (
<div className="avatar-group">
{card.assignees.slice(0, 2).map((assignee) => (
<img
key={assignee.id}
src={assignee.avatar}
alt={assignee.name}
className="avatar avatar--sm avatar--stacked"
/>
))}
{card.assignees.length > 2 && (
<div className="avatar avatar--sm avatar--stacked avatar--count">
+{card.assignees.length - 2}
</div>
)}
</div>
)}

{card.tags && card.tags.length > 0 && (
<div className="tag-list">
{card.tags.map((tag, index) => (
<span key={index} className={`tag tag--sm tag--${tag.color}`}>
{tag.label}
</span>
))}
</div>
)}

{card.dueDate && (
<span
className={`kanban-card__due ${
card.isOverdue ? 'kanban-card__due--warning' : ''
}`}
>
{card.dueDate}
</span>
)}
</div>
</div>
);

KanbanBoard.propTypes = {
initialColumns: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
statusColor: PropTypes.string,
})
).isRequired,
initialCards: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
columnId: PropTypes.string.isRequired,
title: PropTypes.string.isRequired,
description: PropTypes.string,
status: PropTypes.string,
progress: PropTypes.number,
assignees: PropTypes.array,
tags: PropTypes.array,
dueDate: PropTypes.string,
isOverdue: PropTypes.bool,
})
).isRequired,
onCardMove: PropTypes.func,
onCardCreate: PropTypes.func,
};

export default KanbanBoard;

KanbanBoard Usage Example

import React, { useState } from 'react';
import KanbanBoard from './KanbanBoard';
import Modal from './Modal';

function TaskManagement() {
const [showCreateModal, setShowCreateModal] = useState(false);
const [selectedColumn, setSelectedColumn] = useState(null);

const columns = [
{ id: 'todo', title: 'To Do', statusColor: 'inactive' },
{ id: 'inprogress', title: 'In Progress', statusColor: 'warning' },
{ id: 'done', title: 'Done', statusColor: 'success' },
];

const [cards, setCards] = useState([
{
id: '1',
columnId: 'todo',
title: 'Update homepage design',
description: 'Modernize the landing page with new brand guidelines',
assignees: [{ id: 1, name: 'Alice', avatar: '/alice.jpg' }],
tags: [{ label: 'Design', color: 'primary' }],
},
{
id: '2',
columnId: 'inprogress',
title: 'Implement authentication',
progress: 60,
assignees: [{ id: 2, name: 'Bob', avatar: '/bob.jpg' }],
dueDate: 'Due in 2 days',
isOverdue: false,
},
{
id: '3',
columnId: 'done',
title: 'Set up CI/CD pipeline',
status: 'done',
assignees: [{ id: 3, name: 'Charlie', avatar: '/charlie.jpg' }],
},
]);

const handleCardMove = (cardId, newColumnId) => {
console.log(`Card ${cardId} moved to ${newColumnId}`);
// In production, update backend
};

const handleCardCreate = (columnId) => {
setSelectedColumn(columnId);
setShowCreateModal(true);
};

const handleCreateTask = (taskData) => {
const newCard = {
id: Date.now().toString(),
columnId: selectedColumn,
...taskData,
};
setCards([...cards, newCard]);
setShowCreateModal(false);
};

return (
<>
<KanbanBoard
initialColumns={columns}
initialCards={cards}
onCardMove={handleCardMove}
onCardCreate={handleCardCreate}
/>

<Modal
isOpen={showCreateModal}
onClose={() => setShowCreateModal(false)}
title="Create New Task"
footer={
<>
<button
className="button button--secondary"
onClick={() => setShowCreateModal(false)}
>
Cancel
</button>
<button
className="button button--primary"
onClick={() => handleCreateTask({ title: 'New Task' })}
>
Create
</button>
</>
}
>
<div className="form">
<div className="form-field">
<label htmlFor="task-title" className="label">Task Title</label>
<input type="text" id="task-title" className="input" />
</div>
<div className="form-field">
<label htmlFor="task-description" className="label">Description</label>
<textarea id="task-description" className="input" rows="4" />
</div>
</div>
</Modal>
</>
);
}

export default TaskManagement;

Complete App Example with Routing

App.jsx

import React, { useState } from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import Sidebar from './components/Sidebar';
import Dashboard from './pages/Dashboard';
import KanbanBoard from './pages/KanbanBoard';
import Modal from './components/Modal';
import './App.css';

function App() {
const [showCreateProjectModal, setShowCreateProjectModal] = useState(false);

const user = {
firstName: 'Alice',
lastName: 'Johnson',
email: 'alice@example.com',
avatar: '/alice.jpg',
};

const navItems = [
{
id: 'dashboard',
label: 'Dashboard',
href: '/',
icon: '<svg width="20" height="20"><path d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2V9z" stroke="currentColor" fill="none"/></svg>',
},
{
id: 'projects',
label: 'Projects',
href: '/projects',
icon: '<svg width="20" height="20"><path d="M9 2h6l4 4v14a2 2 0 01-2 2H7a2 2 0 01-2-2V4a2 2 0 012-2z" stroke="currentColor" fill="none"/></svg>',
badge: 3,
},
{
id: 'kanban',
label: 'Tasks',
href: '/kanban',
icon: '<svg width="20" height="20"><rect x="3" y="3" width="6" height="14" rx="1" stroke="currentColor" fill="none"/></svg>',
},
{
id: 'team',
label: 'Team',
href: '/team',
icon: '<svg width="20" height="20"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" stroke="currentColor" fill="none"/></svg>',
},
{
id: 'settings',
label: 'Settings',
href: '/settings',
icon: '<svg width="20" height="20"><circle cx="12" cy="12" r="3" stroke="currentColor" fill="none"/></svg>',
divider: true,
},
];

const sidebarFooter = (
<button className="sidebar__user">
<img src={user.avatar} alt="" className="avatar avatar--sm" />
<div className="sidebar__user-info">
<div className="sidebar__user-name">{user.firstName} {user.lastName}</div>
<div className="sidebar__user-email">{user.email}</div>
</div>
<svg className="sidebar__chevron" width="16" height="16">
<path d="M4 6l4 4 4-4" stroke="currentColor" fill="none" />
</svg>
</button>
);

const kanbanColumns = [
{ id: 'todo', title: 'To Do', statusColor: 'inactive' },
{ id: 'inprogress', title: 'In Progress', statusColor: 'warning' },
{ id: 'done', title: 'Done', statusColor: 'success' },
];

const kanbanCards = [
{
id: '1',
columnId: 'todo',
title: 'Update homepage design',
description: 'Modernize the landing page with new brand guidelines',
assignees: [{ id: 1, name: 'Alice', avatar: '/alice.jpg' }],
tags: [{ label: 'Design', color: 'primary' }],
},
];

return (
<Router>
<div className="app-layout">
<Sidebar
logo="/logo.svg"
logoIcon="/icon.svg"
navItems={navItems}
footer={sidebarFooter}
/>

<div className="app-content">
<Routes>
<Route
path="/"
element={
<Dashboard
user={user}
onCreateProject={() => setShowCreateProjectModal(true)}
/>
}
/>
<Route
path="/kanban"
element={
<KanbanBoard
initialColumns={kanbanColumns}
initialCards={kanbanCards}
/>
}
/>
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
</div>

<Modal
isOpen={showCreateProjectModal}
onClose={() => setShowCreateProjectModal(false)}
title="Create New Project"
size="md"
footer={
<>
<button
className="button button--secondary"
onClick={() => setShowCreateProjectModal(false)}
>
Cancel
</button>
<button className="button button--primary">
Create Project
</button>
</>
}
>
<form className="form">
<div className="form-field">
<label htmlFor="project-name" className="label">
Project Name
<span className="label__required">*</span>
</label>
<input
type="text"
id="project-name"
className="input"
required
/>
</div>
<div className="form-field">
<label htmlFor="project-description" className="label">
Description
</label>
<textarea
id="project-description"
className="input"
rows="4"
/>
</div>
</form>
</Modal>
</div>
</Router>
);
}

export default App;

App.css

.app-layout {
display: flex;
min-height: 100vh;
background: var(--gray-50);
}

.app-content {
flex: 1;
min-width: 0;
overflow-x: hidden;
}

/* Loading skeleton */
.loading-skeleton {
padding: 24px;
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}

.skeleton {
background: var(--gray-200);
border-radius: 8px;
}

.skeleton--title {
height: 32px;
width: 300px;
margin-bottom: 24px;
}

.skeleton--stats {
height: 100px;
margin-bottom: 24px;
}

.skeleton--grid {
height: 400px;
}

@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}

This provides complete, production-ready React template implementations with:

  1. Full Dashboard with loading states
  2. Complete KanbanBoard with drag-and-drop
  3. Full app integration with routing
  4. Modal integration
  5. Proper prop types and error handling