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 proxy/middleware

Create or update your proxy.ts or middleware.ts file:

Code

import { unblindProxy } from "@unblind/nextjs/server";
export default unblindProxy((req) => req.cookies.get("userId")?.value);

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 styles

Add Unblind styles to your global tailwind file.

globals.css

@import "@unblind/nextjs/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 { Scope, Timeseries, TimeRange } from '@unblind/nextjs'

export default function Dashboard() {

  return (
    <div>
      <Scope>
        <TimeRange />
        <div className="grid grid-cols-2 gap-4">
          <Timeseries metrics={['system.cpu.utilization']} />
          <Timeseries metrics={['system.memory.usage']} />
        </div>
      </Scope>
    </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

Was this page helpful?