Building Monitoring Dashboards with Next.js
The @unblind/nextjs package provides a complete solution for building customer-facing monitoring dashboards in your Next.js application. It includes a secure server-side proxy, React hooks for data fetching, and pre-built chart components.
Installation
Install the Next.js package, which includes both client and server components:
npm install @unblind/nextjs
Setup
1. Configure the Proxy Handler
The Unblind handler acts as a secure proxy between your frontend and the Unblind API. It automatically injects authentication and tenant context into requests.
Starting with Next.js 16, Middleware is now called Proxy. Ref
1. Configure proxy/middleware
Create or update your proxy.ts or middleware.ts file:
Code
import { unblindProxy } from '@unblind/nextjs/server'
import { NextRequest, NextResponse } from 'next/server'
export async function proxy(request: NextRequest) {
// Handle Unblind API requests
if (request.nextUrl.pathname.startsWith('/api/unblind')) {
// Extract tenant ID from your auth system
// This could be a user ID, organization ID, etc.
const tenantId = request.cookies.get('userId')?.value
// The handler proxies requests to Unblind API with proper auth
const response = await unblindProxy(request, tenantId)
if (response) {
return response
}
}
return NextResponse.next()
}
export const config = {
matcher: '/api/unblind/:path*',
}
2. Environment Variables
Set your Unblind API key in your .env:
.env
UNBLIND_API_KEY=your_api_key_here
2. Add Unblind css
Add Unblind styles to your global tailwind file.
globals.css
@import "@unblind/react/styles.css";
@import "tailwindcss";
/* ... */
4. Wrap Your App with UnblindProvider
The UnblindProvider provides configuration to all Unblind components and hooks.
app/layout.tsx
import { UnblindProvider } from '@unblind/nextjs'
export default function RootLayout({ children }) {
return (
<html>
<body>
<UnblindProvider>
{children}
</UnblindProvider>
</body>
</html>
)
}
Build Your Dashboard
Use the Timeseries component to display metrics:
app/dashboard/page.tsx
'use client'
import { Timeseries } from '@unblind/nextjs'
export default function Dashboard() {
return (
<div className="grid grid-cols-2 gap-4">
<div>
<h3>CPU Usage</h3>
<Timeseries
metrics={['system.cpu.utilization']}
type="area"
/>
</div>
<div>
<h3>Memory Usage</h3>
<Timeseries
metrics={['system.memory.usage']}
type="line"
/>
</div>
<div>
<h3>Request Duration</h3>
<Timeseries
metrics={['http.server.duration']}
groupBy={['http.route']}
operator="p95"
type="bar"
/>
</div>
<div>
<h3>Error Rate</h3>
<Timeseries
metrics={['http.server.request.count']}
attributes={{
'http.status_code': ['500', '503', '504']
}}
type="line"
/>
</div>
</div>
)
}
Advanced Features
Dynamic Time Ranges
Allow users to change the time range dynamically:
app/dashboard/page.tsx
'use client'
import { useState } from 'react'
import { UnblindScope, Timeseries, TimeRange } from '@unblind/nextjs'
export default function Dashboard() {
const [timeRange, setTimeRange] = useState<TimeRange>('6h')
return (
<div>
<select
value={timeRange}
onChange={(e) => setTimeRange(e.target.value as TimeRange)}
>
<option value="1h">Last Hour</option>
<option value="6h">Last 6 Hours</option>
<option value="24h">Last 24 Hours</option>
<option value="7d">Last 7 Days</option>
</select>
<UnblindScope timeRange={timeRange}>
<div className="grid grid-cols-2 gap-4">
<Timeseries metrics={['system.cpu.utilization']} />
<Timeseries metrics={['system.memory.usage']} />
</div>
</UnblindScope>
</div>
)
}
Using Hooks for Custom Components
For more control, use hooks directly:
'use client'
import { useTimeseries, useMetrics } from '@unblind/nextjs'
export function CustomMetricCard() {
// Get list of available metrics
const { list, isLoading: metricsLoading } = useMetrics()
// Fetch timeseries data
const { data, isLoading, hasError } = useTimeseries({
queries: [
{
metrics: ['system.cpu.utilization'],
operator: 'avg',
groupBy: ['host.name'],
}
],
timeRange: '1h',
})
if (isLoading || metricsLoading) return <div>Loading...</div>
if (hasError) return <div>Error loading data</div>
const { series, times, metadata } = data
return (
<div>
{series.map((serie) => (
<div key={serie.metric}>
<h4>{metric.name}</h4>
<p>Latest: {serie.values[serie.values.length - 1]}</p>
</div>
))}
</div>
)
}
Fetching Logs
Use the useLogs hook for log data with infinite scroll:
'use client'
import { useLogs } from '@unblind/nextjs'
export function LogViewer() {
const { logs, isLoading, hasNextPage, fetchNextPage } = useLogs({
timeRange: '1h',
filters: [
{ name: 'severity', value: 'ERROR' },
{ name: 'service.name', value: 'api' }
]
})
return (
<div>
{logs.map((log, i) => (
<div key={i}>
<span>{new Date(log.timestamp).toISOString()}</span>
<span>{log.severity_text}</span>
<span>{log.body}</span>
</div>
))}
{hasNextPage && (
<button onClick={() => fetchNextPage()}>
Load More
</button>
)}
</div>
)
}
Usage Analytics
Track and display usage metrics:
'use client'
import { useUsage } from '@unblind/nextjs'
export function UsageChart() {
const { usage, isLoading } = useUsage({
timeRange: '30d'
})
if (isLoading) return <div>Loading...</div>
return (
<div>
{usage.map((day) => (
<div key={day.date}>
<span>{day.date}</span>
<span>Metrics: {day.metrics.units}</span>
<span>Logs: {day.logs.units} ({day.logs.bytes} bytes)</span>
</div>
))}
</div>
)
}
Refresh Data
Use the useRefresh hook to manually refresh all queries:
'use client'
import { useRefresh } from '@unblind/nextjs'
export function RefreshButton() {
const refresh = useRefresh()
return (
<button onClick={refresh}>
Refresh Dashboard
</button>
)
}
Next Steps
- React Package Guide - Use Unblind with other React frameworks
- API Reference - Explore all available endpoints
- Metrics Guide - Learn about supported metric types