Build Dashboards Fast with Tremor

Build Dashboards Fast with Tremor

Learn how to build dashboards fast using the tremor React library of data visualization components."

Tremor is a React library to build dashboards fast. Its modular components are fully open-source, made by data scientists and software engineers with a sweet spot for design.

In this how to, you’ll learn how to

  • setup tremor in a new or existing RedwoodJS app
  • use tremor components to layout a new dashboard
  • use a chart and card component to visualize static data
  • access a GitHub repo to make your dashboard dynamic using an example RedwoodJS app

Live Demo

See what’s possible with a dynamic dashboard live demo build with RedwoodJS and Tremor.

Cool, right?

Let’s get started!

Create a New RedwoodJS Project

In our terminal, we create a new RedwoodJS project:

yarn create redwood-app my-project --ts

Note: If you already have a RedwoodJS project, you can skip this step and continue with the next section.

If you do not want a TypeScript project, omit the --ts flag.

Important: RedwoodJS prefers yarn over npm because a project is monorepo with api and web workspaces. You will install tremor and other web packages using yarn workspaces.

Use the Redwood setup command to install TailwindCSS, its peer dependencies, and create the tailwind.config.js file.

yarn rw setup ui tailwindcss

Install tremor in the web workspace from your command line via yarn.

yarn workspace web add @tremor/react

Install heroicons version 1.0.6 from your command line via yarn.

yarn workspace web add @heroicons/react@1.0.6

Update tailwind config web/config/tailwind.config.js including the path to the tremor module.

/** @type {import('tailwindcss').Config} */
module.exports = {
  content: [
    'src/**/*.{js,jsx,ts,tsx}',
    '../node_modules/@tremor/**/*.{js,ts,jsx,tsx}',
  ],
  theme: {
    extend: {},
  },
  plugins: [],
}

Note: the path for node_modules is ../ because the web workspace is in a subdirectory of the root directory.

Add a Dashboard Page

Generate a page from your command line.

yarn rw g page dashboard /

You will now have a new page at web/src/pages/DashboardPage/DashboardPage.tsx and web/src/Routes.tsx will have a new route added at:

// web/src/Routes.tsx`

<Route path="/" page={DashboardPage} name="dashboard" />

Add simple area chart to the DashboardPage:

import { Grid, Col, Card, Title, AreaChart } from '@tremor/react'

import { MetaTags } from '@redwoodjs/web'

const DashboardPage = () => {
  const chartdata = [
    {
      date: 'Jan 22',
      SemiAnalysis: 2890,
      'The Pragmatic Engineer': 2338,
    },
    {
      date: 'Feb 22',
      SemiAnalysis: 2756,
      'The Pragmatic Engineer': 2103,
    },
    {
      date: 'Mar 22',
      SemiAnalysis: 3322,
      'The Pragmatic Engineer': 2194,
    },
    {
      date: 'Apr 22',
      SemiAnalysis: 3470,
      'The Pragmatic Engineer': 2108,
    },
    {
      date: 'May 22',
      SemiAnalysis: 3475,
      'The Pragmatic Engineer': 1812,
    },
    {
      date: 'Jun 22',
      SemiAnalysis: 3129,
      'The Pragmatic Engineer': 1726,
    },
  ]

  const dataFormatter = (number: number) => {
    return '$ ' + Intl.NumberFormat('us').format(number).toString()
  }

  return (
    <div className="m-12">
      <MetaTags title="Dashboard" description="Dashboard page" />

      <h1 className="text-2xl mb-12">Dashboard</h1>

      <Grid numCols={1} numColsSm={2} numColsLg={3} className="my-8 gap-6">
        <Col numColSpan={1} numColSpanLg={3}>
          <Card>
            <Title>Newsletter revenue over time (USD)</Title>
            <AreaChart
              className="h-72 mt-4"
              data={chartdata}
              index="date"
              categories={['SemiAnalysis', 'The Pragmatic Engineer']}
              colors={['indigo', 'green']}
              valueFormatter={dataFormatter}
            />
          </Card>
        </Col>
      </Grid>
    </div>
  )
}

export default DashboardPage

Start your RedwoodJS development server

yarn rw dev

Your app will start up and you should see the Dashboard page with an area with two Newsletter revenue over time (USD) data series.

Add a new component for a KPI Card

Generate a component for a KPI (Key Performance Indicator) from your command line.

yarn rw g component KpiCard

You will now have a new React component at /web/src/components/KpiCard/KpiCard.tsx.

Update the KpiCard component to import the Card component and assemble a card using its default
styling.

To create our first KPI, we import the Metric and Text component and place them within the card component. We use Tailwind CSS’ utilities in the className property to reduce the card’s width and to center it horizontally.

To make our KPI card more insightful, we add a ProgressBar, providing
contextual details about our metric. To align both text elements, we also import
the Flex component.

// /web/src/components/KpiCard/KpiCard.tsx

import {
  BadgeDelta,
  DeltaType,
  Card,
  Flex,
  Metric,
  ProgressBar,
  Text,
} from '@tremor/react'

export type Kpi = {
  title: string
  metric: string
  progress: number
  metricTarget: string
  delta: string
  deltaType: DeltaType
}

interface Props {
  kpi: Kpi
}

const KpiCard = ({ kpi }: Props) => {
  return (
    <Card className="max-w-lg">
      <Flex alignItems="start">
        <div>
          <Text>{kpi.title}</Text>
          <Metric>{kpi.metric}</Metric>
        </div>
        <BadgeDelta deltaType={kpi.deltaType}>{kpi.delta}</BadgeDelta>
      </Flex>
      <Flex className="mt-4">
        <Text className="truncate">{`${kpi.progress}% (${kpi.metric})`}</Text>
        <Text>{kpi.metricTarget}</Text>
      </Flex>
      <ProgressBar percentageValue={kpi.progress} className="mt-2" />
    </Card>
  )
}

export default KpiCard

Add the KPI Card component to your Dashboard

Import the KpiCard component and Kpi type.

import KpiCard from 'src/components/KpiCard/KpiCard' // 👈 Import the KpiCard component
import type { Kpi } from 'src/components/KpiCard/KpiCard' // 👈 Import the Kpi type

Next, create the kpi data collection with sample data

 const kpis: Kpi[] = [ // 👈 Create some sample KPI data
    {
      title: 'Sales',
      metric: '$ 12,699',
      progress: 15.9,
      metricTarget: '$ 80,000',
      delta: '13.2%',
      deltaType: 'moderateIncrease',
    },
    {
      title: 'Profit',
      metric: '$ 45,564',
      progress: 36.5,
      metricTarget: '$ 125,000',
      delta: '23.9%',
      deltaType: 'increase',
    },
    {
      title: 'Customers',
      metric: '1,072',
      progress: 53.6,
      metricTarget: '2,000',
      delta: '10.1%',
      deltaType: 'moderateDecrease',
    },
  ]

Then iterate over the collection to add a KpiCard inside new Col for each KPI data item:

  {kpis.map((kpi, i) => (
    <Col key={i} numColSpan={1}>
      <KpiCard kpi={kpi} />
    </Col>
  ))}

Your Dashboard page should now look like:

import { Grid, Col, Card, Title, AreaChart } from '@tremor/react'

import { MetaTags } from '@redwoodjs/web'

import KpiCard from 'src/components/KpiCard/KpiCard' // 👈 Import the KpiCard component
import type { Kpi } from 'src/components/KpiCard/KpiCard' // 👈 Import the Kpi type

const DashboardPage = () => {
  const chartdata = [
    {
      date: 'Jan 22',
      SemiAnalysis: 2890,
      'The Pragmatic Engineer': 2338,
    },
    {
      date: 'Feb 22',
      SemiAnalysis: 2756,
      'The Pragmatic Engineer': 2103,
    },
    {
      date: 'Mar 22',
      SemiAnalysis: 3322,
      'The Pragmatic Engineer': 2194,
    },
    {
      date: 'Apr 22',
      SemiAnalysis: 3470,
      'The Pragmatic Engineer': 2108,
    },
    {
      date: 'May 22',
      SemiAnalysis: 3475,
      'The Pragmatic Engineer': 1812,
    },
    {
      date: 'Jun 22',
      SemiAnalysis: 3129,
      'The Pragmatic Engineer': 1726,
    },
  ]

  const kpis: Kpi[] = [ // 👈 Create some sample KPI data
    {
      title: 'Sales',
      metric: '$ 12,699',
      progress: 15.9,
      metricTarget: '$ 80,000',
      delta: '13.2%',
      deltaType: 'moderateIncrease',
    },
    {
      title: 'Profit',
      metric: '$ 45,564',
      progress: 36.5,
      metricTarget: '$ 125,000',
      delta: '23.9%',
      deltaType: 'increase',
    },
    {
      title: 'Customers',
      metric: '1,072',
      progress: 53.6,
      metricTarget: '2,000',
      delta: '10.1%',
      deltaType: 'moderateDecrease',
    },
  ]

  const dataFormatter = (number: number) => {
    return '$ ' + Intl.NumberFormat('us').format(number).toString()
  }

  return (
    <div className="m-12">
      <MetaTags title="Dashboard" description="Dashboard page" />

      <h1 className="mb-12 text-2xl">Dashboard</h1>

      <Grid numCols={1} numColsSm={2} numColsLg={3} className="my-8 gap-6">
        {kpis.map((kpi, i) => (
          <Col key={i} numColSpan={1}>
            <KpiCard kpi={kpi} />
          </Col>
        ))}
       <Col numColSpan={1} numColSpanLg={3}>
          <Card>
            <Title>Newsletter revenue over time (USD)</Title>
            <AreaChart
              className="mt-4 h-72"
              data={chartdata}
              index="date"
              categories={['SemiAnalysis', 'The Pragmatic Engineer']}
              colors={['indigo', 'green']}
              valueFormatter={dataFormatter}
            />
          </Card>
        </Col>
      </Grid>
    </div>
  )
}

export default DashboardPage

Congratulations! You made your first dashboard.

Next Steps

Now that you have a Dashboard

  1. Explore the other components and blocks that you can use to showcase your data

  2. Learn how to make a dynamic dashboard using RedwoodJS cells to fetch data from a Prisma-backed database using GraphQL.

  3. See a dynamic dashboard live demo!

3 Likes

Hey, community:

Would you like to see a RedwoodJS setup ui command that:

  • installs tremor
  • sets up tailwinds
  • builds a static dashboard with components as a new route and page

?

If so, leave comments and or likes!

2 Likes

Hi,

Many thanks for this tutorial but unfortunately deployment does not really fit to the new bundler VIte. Any clue on this, and maybe which settings or files need to be adapted to use Vite?

Hi @matbgn could you elaborate on what you mean by “deployment”?

I haven’t started using vite yet.

What parts of the package installation and using the components doesn’t work?

Yes of course, sorry for the lack of clarity.

In fact, it’s quite simple: the aim is to deploy, for example, in a docker image built locally, to then be able to deploy the image on a CI.

Unfortunately, after several attempts with the redwood.toml configuration and other files, the only solution that worked was to switch back to webpack.

Which tells me that it’s probably a matter of small adjustments to get it to work with Vite. But as Webpack will be deprecated in the next major version of RedwoodJS and we’ve just started this test project with Tremor, we’re interested in the solution if anyone can find the right settings.

Ok so this is a Docker issue and not a tremor component example issue?

No, I would rather say a lack of direct connection (straightforward) between Redwood and Tremor with the new bundler.

Hi @matbgn

Do you have any error messages you can share with us?
Or, even better, a small reproduction project we can look at?

Thanks!

Just this one you provided in the tutorial, with regards to the yarn rw setup vite of course. This would already not work with the build/serve process yarn rw build web && yarn rw serve web no need to go further with docker packaging I guess because at this step all the assets from Tremor/Tailwind won’t be loaded.

Hi, redwoodjs dev team, I really like this feature! It’d be even better if it can generate a data grid page that supports basic filter/sort/search operations for users to explore the underlying data. :clap: