With this post, we'll use Recharts with an Angular frontend and a Node.js backend to build a dashboard with stunning charts.

To concentrate on the UI for the time being, we will use hardcoded data. Real data can be connected later.

1.1 Install Angular CLI
Make sure you have Node.js installed. Then install Angular CLI globally:
npm install -g @angular/cli
1.2 Create a New Angular Project
ng new dashboard-app
cd dashboard-app
1.3 Create the Dashboard Component
We’ll create a DashboardComponent with hardcoded data for revenue, user activity, and sales. The component uses Chart.js to render three charts: a line chart for revenue, a line chart for user activity, and a bar chart for sales by category.
Here’s the code for dashboard.component.ts:
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Chart } from 'chart.js/auto';
interface ChartData {
month: string;
revenue: number;
}
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [CommonModule],
template: `
<div class="dashboard-container p-8">
<h1 class="text-4xl font-bold mb-8">Dashboard</h1>
<!-- Stats Grid -->
<div class="stats-grid mb-8">
<div class="stat-card" *ngFor="let stat of stats">
<div class="stat-header">
<span class="stat-title">{{stat.title}}</span>
<i class="stat-icon" [class]="stat.icon"></i>
</div>
<div class="stat-value">{{stat.value}}</div>
</div>
</div>
<!-- Charts -->
<div class="charts-grid mb-8">
<div class="chart-container">
<canvas id="revenueChart"></canvas>
</div>
<div class="chart-container">
<canvas id="userActivityChart"></canvas>
</div>
</div>
<div class="chart-container">
<canvas id="salesChart"></canvas>
</div>
</div>
`,
styles: [`
.dashboard-container {
max-width: 1400px;
margin: 0 auto;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 1rem;
}
.stat-card {
background: white;
padding: 1.5rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.stat-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 0.5rem;
}
.stat-title {
font-size: 0.875rem;
color: #64748b;
}
.stat-value {
font-size: 1.5rem;
font-weight: bold;
color: #1e293b;
}
.charts-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 1rem;
}
.chart-container {
background: white;
padding: 1rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
`]
})
export class DashboardComponent implements OnInit {
stats = [
{ title: 'Total Revenue', value: '$443,000', icon: 'bi bi-currency-dollar' },
{ title: 'Average Order', value: '$245', icon: 'bi bi-credit-card' },
{ title: 'Total Customers', value: '12.5K', icon: 'bi bi-people' },
{ title: 'Total Orders', value: '8.2K', icon: 'bi bi-cart' }
];
revenueData = {
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
datasets: [{
label: 'Revenue',
data: [24000, 26000, 32000, 28000, 35000, 42000, 39000, 45000, 48000, 52000, 49000, 55000],
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
}]
};
userActivityData = {
labels: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
datasets: [
{
label: 'Active Users',
data: [3200, 3800, 4200, 3900, 3600, 2900, 2800],
borderColor: 'rgb(75, 192, 192)',
tension: 0.1
},
{
label: 'New Users',
data: [1400, 1600, 1800, 1500, 1300, 1000, 900],
borderColor: 'rgb(255, 99, 132)',
tension: 0.1
}
]
};
salesData = {
labels: ['Electronics', 'Clothing', 'Books', 'Home', 'Sports'],
datasets: [{
label: 'Sales by Category',
data: [42000, 28000, 15000, 22000, 18000],
backgroundColor: [
'rgba(255, 99, 132, 0.5)',
'rgba(54, 162, 235, 0.5)',
'rgba(255, 206, 86, 0.5)',
'rgba(75, 192, 192, 0.5)',
'rgba(153, 102, 255, 0.5)'
]
}]
};
constructor() {}
ngOnInit(): void {
this.createCharts();
}
private createCharts(): void {
// Revenue Chart
new Chart('revenueChart', {
type: 'line',
data: this.revenueData,
options: {
responsive: true,
maintainAspectRatio: false
}
});
// User Activity Chart
new Chart('userActivityChart', {
type: 'line',
data: this.userActivityData,
options: {
responsive: true,
maintainAspectRatio: false
}
});
// Sales Chart
new Chart('salesChart', {
type: 'bar',
data: this.salesData,
options: {
responsive: true,
maintainAspectRatio: false
}
});
}
}
Code in app.component.ts
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-root',
standalone: true,
imports: [RouterOutlet, CommonModule],
template: `
<div class="app-container">
<router-outlet></router-outlet>
</div>
`,
styles: [`
.app-container {
min-height: 100vh;
background-color: #f8f9fa;
padding: 1rem;
}
`]
})
export class AppComponent {
title = 'dashboard';
}
Code for not-found.component.ts
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-not-found',
standalone: true,
imports: [CommonModule],
template: `
<div class="not-found-container">
<div class="error-card">
<div class="error-header">
<i class="bi bi-exclamation-circle text-red-500"></i>
<h1>404 Page Not Found</h1>
</div>
<p>The page you're looking for doesn't exist.</p>
</div>
</div>
`,
styles: [`
.not-found-container {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background-color: #f8f9fa;
padding: 1rem;
}
.error-card {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
max-width: 400px;
width: 100%;
}
.error-header {
display: flex;
align-items: center;
gap: 0.5rem;
margin-bottom: 1rem;
}
h1 {
font-size: 1.5rem;
font-weight: bold;
color: #1e293b;
}
p {
color: #64748b;
}
`]
})
export class NotFoundComponent {}
import { Routes } from '@angular/router';
export const routes: Routes = [
{
path: '',
loadComponent: () => import('./dashboard/dashboard.component')
.then(m => m.DashboardComponent)
},
{
path: '**',
loadComponent: () => import('./not-found/not-found.component')
.then(m => m.NotFoundComponent)
}
];
Server Side code - index.js
import express, { type Request, Response, NextFunction } from "express";
import { registerRoutes } from "./routes";
import path from "path";
import { fileURLToPath } from "url";
import { dirname } from "path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
// Logging middleware
app.use((req, res, next) => {
const start = Date.now();
const path = req.path;
let capturedJsonResponse: Record<string, any> | undefined = undefined;
const originalResJson = res.json;
res.json = function (bodyJson, ...args) {
capturedJsonResponse = bodyJson;
return originalResJson.apply(res, [bodyJson, ...args]);
};
res.on("finish", () => {
const duration = Date.now() - start;
if (path.startsWith("/api")) {
let logLine = `${req.method} ${path} ${res.statusCode} in ${duration}ms`;
if (capturedJsonResponse) {
logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`;
}
if (logLine.length > 80) {
logLine = logLine.slice(0, 79) + "…";
}
console.log(`${new Date().toLocaleTimeString()} [express] ${logLine}`);
}
});
next();
});
(async () => {
const server = await registerRoutes(app);
app.use((err: any, _req: Request, res: Response, _next: NextFunction) => {
const status = err.status || err.statusCode || 500;
const message = err.message || "Internal Server Error";
res.status(status).json({ message });
throw err;
});
// Serve static files from the Angular build output directory
const distPath = path.resolve(__dirname, "..", "dist", "public");
app.use(express.static(distPath));
// Always return index.html for any non-API routes (Angular routing)
app.get("*", (_req, res) => {
res.sendFile(path.join(distPath, "index.html"));
});
const port = 5000;
server.listen({
port,
host: "0.0.0.0",
reusePort: true,
}, () => {
console.log(`${new Date().toLocaleTimeString()} [express] serving on port ${port}`);
});
})();
angular.json file
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"dashboard": {
"projectType": "application",
"schematics": {},
"root": "client",
"sourceRoot": "client/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/public",
"index": "client/src/index.html",
"main": "client/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "tsconfig.json",
"assets": [
"client/src/favicon.ico",
"client/src/assets"
],
"styles": [
"client/src/styles.css",
"node_modules/bootstrap-icons/font/bootstrap-icons.css"
],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"fileReplacements": [
{
"replace": "client/src/environments/environment.ts",
"with": "client/src/environments/environment.prod.ts"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "dashboard:build:production"
},
"development": {
"browserTarget": "dashboard:build:development"
}
},
"defaultConfiguration": "development"
}
}
}
}
}

This post showed how to use Angular and Node.js to create an eye-catching dashboard. We produced adaptable and interactive charts that make data come to life by utilizing Chart.js. This stack is perfect for creating scalable apps because of Angular's modular design and Node.js's speed. Additional charts, authentication, and the integration of real-time data sources are possible future enhancements. You may design dashboards of expert quality that meet your demands with this base.