---
title: "A step-by-step guide to build a real-time dashboard"
excerpt: "Build a real-time dashboard step by step. No fluff, no theory. Just working code and architecture decisions explained clearly."
authors: "Joe Karlsson"
categories: "I Built This!"
createdOn: "2023-09-25 00:00:00"
publishedOn: "2023-09-05 00:00:00"
updatedOn: "2026-01-15 00:00:00"
status: "published"
---

<p>Can you imagine shipping a new user-facing dashboard only to have your users met with a visualization that takes several seconds or even <em>minutes</em> to load? No way, right? If you're learning how to create a dashboard web application, this is exactly the problem you need to solve. Your users would get frustrated by the opportunities missed, efficiencies destroyed, and decisions delayed based on outdated information and a horrible user experience.</p><p>Sadly, this is the status quo for many who build dashboards into their products. If you don’t know how to build real-time data architectures, you’ll be stuck with inefficient, legacy business intelligence platforms that can’t keep pace with user-facing features.</p><p>The contrast of this scenario with today's <a href="https://www.tinybird.co/blog-posts/real-time-analytics-a-definitive-guide">real-time analytics landscape</a> couldn't be more stark, underlining just how vital it is to give your users immediate access to data analytics.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tinybird-blog.ghost.io/content/images/2023/09/64f2397eb598cc2794d09e8e_ai0Pgs-GDTKhK0NJ6ebhQZqy4j2mAiNdtQpAJJng8s3Ca4KMGuo3vX8uMGtlFl9OEl1hl6uougnvVCDsl8NfWh5G_v98P7ke2yerYA3WvNiE7Q07RohoWbuzOMbft6LwDYTuIr_C08xS3yk1ju5cdpg-7.png" class="kg-image" alt="A &quot;Hide the Pain, Harold&quot; meme with the text &quot;My boss looking at my real-time dashboard instead of a spreadsheet.&quot;" loading="lazy" width="1080" height="1080" srcset="https://tinybird-blog.ghost.io/content/images/size/w600/2023/09/64f2397eb598cc2794d09e8e_ai0Pgs-GDTKhK0NJ6ebhQZqy4j2mAiNdtQpAJJng8s3Ca4KMGuo3vX8uMGtlFl9OEl1hl6uougnvVCDsl8NfWh5G_v98P7ke2yerYA3WvNiE7Q07RohoWbuzOMbft6LwDYTuIr_C08xS3yk1ju5cdpg-7.png 600w, https://tinybird-blog.ghost.io/content/images/size/w1000/2023/09/64f2397eb598cc2794d09e8e_ai0Pgs-GDTKhK0NJ6ebhQZqy4j2mAiNdtQpAJJng8s3Ca4KMGuo3vX8uMGtlFl9OEl1hl6uougnvVCDsl8NfWh5G_v98P7ke2yerYA3WvNiE7Q07RohoWbuzOMbft6LwDYTuIr_C08xS3yk1ju5cdpg-7.png 1000w, https://tinybird-blog.ghost.io/content/images/2023/09/64f2397eb598cc2794d09e8e_ai0Pgs-GDTKhK0NJ6ebhQZqy4j2mAiNdtQpAJJng8s3Ca4KMGuo3vX8uMGtlFl9OEl1hl6uougnvVCDsl8NfWh5G_v98P7ke2yerYA3WvNiE7Q07RohoWbuzOMbft6LwDYTuIr_C08xS3yk1ju5cdpg-7.png 1080w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">Release the pain, Harold.</span></figcaption></figure><p>In this post, we're going to build a real-time analytics dashboard from scratch within a user-facing application. If you want to know how to create a dashboard web application, this guide is for you. And we're not doing it the old-fashioned way, oh no! We're using some of my personal favorite tech out there: Tinybird, Tremor, and Next.js.</p><p>This post will walk through all of the steps to building a real-time dashboard using just these 3 tools. You can follow along here without any prior knowledge or resources, or if you’d like to work with and augment an existing project, you can clone this <a href="https://github.com/tinybirdco/signatures-dashboard">GitHub repo</a> (which is the culmination of this guide, and then some).</p>
<!--kg-card-begin: html-->
<blockquote>In this post you'll learn to create a real-time dashboard from scratch (for free) using Tinybird, Tremor, and Next.js.</blockquote>
<!--kg-card-end: html-->
<p>And by the way, you can do all of this for free. 🤑</p><p>Before we jump in, let’s talk about what we mean by a “real-time dashboard” and why most dashboards aren’t “real-time.” If you’re just here for the tutorial, you can <a href="#tutorial--building-a-real-time-data-analytics-dashboard">skip ahead</a>.</p><h2 id="what-is-a-real-time-dashboard">What is a real-time dashboard?</h2><p>A real-time analytics dashboard is an interactive <a href="https://www.tinybird.co/blog-posts/real-time-data-visualization">real-time data visualization</a> that displays continually updated metrics. It incorporates data that is just seconds old, refreshes almost instantaneously, and can support many concurrent viewers at once. Unlike traditional business intelligence dashboards that update on a periodic or batch basis, a web based dashboard pulls in data as it is created, processed, or changed, providing an up-to-the-second snapshot of a system or process.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tinybird-blog.ghost.io/content/images/2023/09/64f2397e26737f58a3a8e5b2_RJqiXc4ZybopbNjI9ySkEeBL37h3SMQ7QE5yqkmAjfHKNho7ZJj08kuF7cu0OQL8WQ1izPa6wV4GCjiImJdYM7bRlAAPVRqkEypjP-u2caVYBF3wXHp8y2CHMCm55uNR3POgx8hW5brl67iCM0HGpJM-7.gif" class="kg-image" alt="" loading="lazy" width="1140" height="720" srcset="https://tinybird-blog.ghost.io/content/images/size/w600/2023/09/64f2397e26737f58a3a8e5b2_RJqiXc4ZybopbNjI9ySkEeBL37h3SMQ7QE5yqkmAjfHKNho7ZJj08kuF7cu0OQL8WQ1izPa6wV4GCjiImJdYM7bRlAAPVRqkEypjP-u2caVYBF3wXHp8y2CHMCm55uNR3POgx8hW5brl67iCM0HGpJM-7.gif 600w, https://tinybird-blog.ghost.io/content/images/size/w1000/2023/09/64f2397e26737f58a3a8e5b2_RJqiXc4ZybopbNjI9ySkEeBL37h3SMQ7QE5yqkmAjfHKNho7ZJj08kuF7cu0OQL8WQ1izPa6wV4GCjiImJdYM7bRlAAPVRqkEypjP-u2caVYBF3wXHp8y2CHMCm55uNR3POgx8hW5brl67iCM0HGpJM-7.gif 1000w, https://tinybird-blog.ghost.io/content/images/2023/09/64f2397e26737f58a3a8e5b2_RJqiXc4ZybopbNjI9ySkEeBL37h3SMQ7QE5yqkmAjfHKNho7ZJj08kuF7cu0OQL8WQ1izPa6wV4GCjiImJdYM7bRlAAPVRqkEypjP-u2caVYBF3wXHp8y2CHMCm55uNR3POgx8hW5brl67iCM0HGpJM-7.gif 1140w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">A real-time dashboard should serve fresh data quickly to many concurrent users.</span></figcaption></figure><p>The primary components of a real-time dashboard include:</p><ol><li><strong>Data Sources</strong>: The real-time feeds of information from various systems, services, and devices. Examples include sensor readings, user activity on a website, sales transactions, or social media interactions.</li><li><strong>Data Processing Engine</strong>: The system that aggregates, filters, and transforms the raw data from various sources into a format that can be consumed by a frontend application.</li><li><strong>Visualization Layer</strong>: The frontend app that brings the data to life through graphs, charts, maps, and other visuals. In a real-time dashboard, these visual components update near-instantaneously, reflecting the most current state of the data sources.</li><li><strong>Interactive Controls</strong>: Components by which users can interact with real-time dashboards, such as by adjusting filters, drilling down into detailed views, or setting alerts for specific conditions. These features empower users to explore data in more depth and respond to changes more swiftly.</li></ol><h3 id="why-are-most-dashboards-slow">Why are most dashboards slow?</h3><p>Let’s be honest, most dashboards aren’t of the “real-time” variety. But why?</p><figure class="kg-card kg-image-card"><img src="https://tinybird-blog.ghost.io/content/images/2023/09/64f2397e81e29ec2e46df327_wvTTRXvkirb_mqcXkmQicdTWQE3ZfbkCc7q29ZFbzcutFWklxwxD1EDUdUnYK1jl9DmpHu8pRjEGo961C6wdPc3j1utD7JIw4JJRAfxkVFqIeMK-IFjHgjZ_vv6hvQVPegp3zhVejRzYDtGvVj-Tx5o-7.png" class="kg-image" alt="A meme with a  skeleton next to the text &quot;Me, waiting for the data to be extracted, transformed, and loaded.&quot;" loading="lazy" width="1080" height="1080" srcset="https://tinybird-blog.ghost.io/content/images/size/w600/2023/09/64f2397e81e29ec2e46df327_wvTTRXvkirb_mqcXkmQicdTWQE3ZfbkCc7q29ZFbzcutFWklxwxD1EDUdUnYK1jl9DmpHu8pRjEGo961C6wdPc3j1utD7JIw4JJRAfxkVFqIeMK-IFjHgjZ_vv6hvQVPegp3zhVejRzYDtGvVj-Tx5o-7.png 600w, https://tinybird-blog.ghost.io/content/images/size/w1000/2023/09/64f2397e81e29ec2e46df327_wvTTRXvkirb_mqcXkmQicdTWQE3ZfbkCc7q29ZFbzcutFWklxwxD1EDUdUnYK1jl9DmpHu8pRjEGo961C6wdPc3j1utD7JIw4JJRAfxkVFqIeMK-IFjHgjZ_vv6hvQVPegp3zhVejRzYDtGvVj-Tx5o-7.png 1000w, https://tinybird-blog.ghost.io/content/images/2023/09/64f2397e81e29ec2e46df327_wvTTRXvkirb_mqcXkmQicdTWQE3ZfbkCc7q29ZFbzcutFWklxwxD1EDUdUnYK1jl9DmpHu8pRjEGo961C6wdPc3j1utD7JIw4JJRAfxkVFqIeMK-IFjHgjZ_vv6hvQVPegp3zhVejRzYDtGvVj-Tx5o-7.png 1080w" sizes="(min-width: 720px) 720px"></figure><p>The problem is the underlying architecture and the manner in which the data is handled. Here are the main reasons that dashboards are slow:</p><ol><li><strong>Batch ETL (Extract, Transform, Load) Processes</strong>: Many dashboards rely on batch ETL processes that collect, transform, and load data at specific intervals. These time-bound processes result in data that isn’t fresh. It doesn’t matter if a dashboard can refresh in 50 milliseconds if the data it’s showing is hours or days old.</li><li><strong>Complex Business Intelligence (BI) Tools</strong>: BI tools were designed for a small handful of users to run and visualize complex analytical queries over a database or data warehouse. While they are powerful for internal reporting and dashboarding, they tend to be slow. They’re not optimized for user-facing applications and often struggle with high query latencies and minimal user concurrency.</li><li><strong>Poorly Configured Data Stack</strong>: Most databases, data warehouses, and data processing layers aren’t optimized for <a href="https://www.tinybird.co/blog-posts/real-time-analytics-a-definitive-guide">real-time analytics</a>. Things like inefficient indexing, <a href="https://www.tinybird.co/blog-posts/when-to-use-columnar-database">row-based storage</a>, improper data partitioning, and lack of in-memory processing can all cause bottlenecks in the data flow.</li><li><strong>Poorly Designed Queries</strong>: Inefficient or poorly constructed queries can significantly slow down data retrieval. Bad indexing, heavy joins, and full table scans all contribute to slow dashboards.</li><li><strong>Lack of Scalability</strong>: Real-time dashboards need to be able to scale with big data and with many concurrent users. A modern real-time dashboard must be built with scalability in mind to ensure that performance does not degrade as demand grows.</li></ol>
<!--kg-card-begin: html-->
<blockquote>Most dashboards are slow because the underlying data pipelines are slow.</blockquote>
<!--kg-card-end: html-->
<p>To build real-time dashboards, you need a real-time streaming data architecture. For more information on building such an architecture, <a href="https://www.tinybird.co/blog-posts/real-time-streaming-data-architectures-that-scale">read this post</a>.</p><h2 id="tutorial-building-a-real-time-data-analytics-dashboard">Tutorial: Building a real-time data analytics dashboard</h2><p>Okay, so what are we building? Imagine you’re a DocuSign competitor. You’re building a SaaS to disrupt the document signature space, and as a part of that, you want to give your users a real-time data analytics dashboard so they can monitor how, when, where, and what is happening with their documents in real time.</p>
<!--kg-card-begin: html-->
<blockquote>We're building a real-time dashboard for a hypothetical DocuSign competitor.</blockquote>
<!--kg-card-end: html-->
<p>Let’s build that dashboard.</p><p>To do so, we'll be using:</p><ul><li><a href="https://www.tinybird.co/">Tinybird</a> for real-time data ingestion, real-time data processing, and real-time APIs.</li><li><a href="https://www.tremor.so/">Tremor</a> components for the data visualization. It turns those numbers and statistics into something beautiful.</li><li><a href="https://nextjs.org/">Next.js</a> as a fully-featured React framework. It ensures everything looks slick and runs smoothly.</li></ul>
<!--kg-card-begin: html-->
<div class="tip-box"><div class="tip-box-container"><div class="tip-box-title">What will <em>you</em> build?</div><div class="tip-box-content">This is just an example project. The specific use case is irrelevant. You can take the structure we're about to explore and apply it to pretty much any use case you can dream of. So follow along, and learn how to build <em>any </em>real-time dashboard.</div></div></div>
<!--kg-card-end: html-->
<h3 id="the-tech-stack">The Tech Stack</h3><p>Here's the flow of what we’re building today:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://tinybird-blog.ghost.io/content/images/2023/09/64f2397eadd7734c2bfc8a3d_cXerql84I3n75Bwq3x5cfiQNpn-OUSINGJFwzA7jmaSlj6n2cOfTEcwO-KnXVWEoUx7FBUy5gZqcabgni5oBihWqAfxTviEGAmBhfu8ya2LEjY9DO1R4sq189-ZhFnCFGZulcinO2iP9iDtdnr9KBzg-7.png" class="kg-image" alt="The basic architecture used in this tutorial to build a real-time dashboard" loading="lazy" width="1600" height="855" srcset="https://tinybird-blog.ghost.io/content/images/size/w600/2023/09/64f2397eadd7734c2bfc8a3d_cXerql84I3n75Bwq3x5cfiQNpn-OUSINGJFwzA7jmaSlj6n2cOfTEcwO-KnXVWEoUx7FBUy5gZqcabgni5oBihWqAfxTviEGAmBhfu8ya2LEjY9DO1R4sq189-ZhFnCFGZulcinO2iP9iDtdnr9KBzg-7.png 600w, https://tinybird-blog.ghost.io/content/images/size/w1000/2023/09/64f2397eadd7734c2bfc8a3d_cXerql84I3n75Bwq3x5cfiQNpn-OUSINGJFwzA7jmaSlj6n2cOfTEcwO-KnXVWEoUx7FBUy5gZqcabgni5oBihWqAfxTviEGAmBhfu8ya2LEjY9DO1R4sq189-ZhFnCFGZulcinO2iP9iDtdnr9KBzg-7.png 1000w, https://tinybird-blog.ghost.io/content/images/2023/09/64f2397eadd7734c2bfc8a3d_cXerql84I3n75Bwq3x5cfiQNpn-OUSINGJFwzA7jmaSlj6n2cOfTEcwO-KnXVWEoUx7FBUy5gZqcabgni5oBihWqAfxTviEGAmBhfu8ya2LEjY9DO1R4sq189-ZhFnCFGZulcinO2iP9iDtdnr9KBzg-7.png 1600w" sizes="(min-width: 720px) 720px"><figcaption><span style="white-space: pre-wrap;">In this tutorial, we'll use Tinybird to capture event streams, process them with SQL, and expose the transformations as real-time APIs. Then we'll use Tremor components in a Next.js app to build a beautiful, responsive, real-time dashboard.</span></figcaption></figure><ol><li><strong>Events</strong> (like a document being sent, signed, or received) will be sent to the <a href="https://www.tinybird.co/docs/ingest/events-api.html">Tinybird Events API</a>, an HTTP streaming endpoint that captures events and writes them to a <a href="https://www.tinybird.co/blog-posts/what-is-a-columnar-database" rel="noreferrer">columnar base</a> optimized for <a href="https://www.tinybird.co/blog-posts/real-time-analytics-a-definitive-guide" rel="noreferrer">real-time analytics</a>.</li><li><strong>Tinybird </strong>is a <a href="https://www.tinybird.co/blog-posts/real-time-data-platforms">real-time data platform</a> that we can use to build real-time metrics with SQL and instantly publish them as APIs.</li><li><strong>Tremor</strong> will then poll the API endpoints we publish in Tinybird and visualize the real-time metrics as beautiful visualizations.</li><li><strong>Next.js</strong> as a React framework for building our dashboard.</li></ol><h2 id="how-to-build-a-real-time-dashboard-from-scratch">How to build a real-time dashboard from scratch</h2><p>To build a real-time dashboard from scratch, you’ll follow these steps:</p><ul><li><a href="#step-0--install-prerequisites">Step 0: Install prerequisites</a></li><li><a href="#step-1--create-a-mock-data-stream">Step 1: Create a mock data stream</a></li><li><a href="#step-2--build-dashboard-metrics-with-sql-in-tinybird">Step 2: Build dashboard metrics with SQL in Tinybird</a></li><li><a href="#step-3--publish-metrics-as-apis-using-tinybird">Step 3: Publish metrics as APIs using Tinybird</a></li><li><a href="#step-4--create-real-time-dashboard-components-with-tremor-and-next-js">Step 4: Create real-time dashboard components with Tremor and Next.js</a></li></ul><h3 id="step-0-install-prerequisites">Step 0: Install Prerequisites</h3><p>Before you get started, you’ll need to have the following prerequisites installed:</p><ul><li>Node.js (version 18 or above)</li><li>Python (version 3.8 or above)</li></ul><p>For information on installing those, check out their docs: <a href="https://nodejs.org/en/download">Node.js</a> and <a href="https://www.python.org/downloads/">Python</a>.</p><h4 id="initialize-your-nextjs-project">Initialize your Next.js project</h4><p>Once you have those installed, create a new Next.js app. In this demo, I’ll be using plain JavaScript files (no TypeScript) and Tailwind CSS.</p><figure class="kg-card kg-embed-card"><iframe width="100%" src="https://snippets.tinybird.co/XQAAAAKbAAAAAAAAAABBKUqGk9nLKvj6K2g-jZn2gsY6uO5kfncXZGJpDtOZNnbI7Xj70vqtVET6y6jdZkUUnj9zeLMbepoSMAFFiYqCO0fowu4HJvQU7QJZbKnk0LqE3GQdIGWMRa-w3BS27IXoMGPPhGKUnrd7SZyVujWukOSmuxqwuYj9_y-QA7RwNlDO45FA3cTpvmqnV_xMz4A/embed"></iframe></figure><p>When prompted, select “Yes” for App Router and "No" to customizing the default import alias</p><p>Next, create some folders in your Next.js project. We’ll use these for the Tinybird resources we create later.</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAKMAAAAAAAAAABBKUqGk9nLKvqsCWfZGOBayhaiV6El3dduoYusi1RHwBIwqQNuhrNtu41dIc1-e_krUk_0SNd2QPLpEs-0DMqy0Y6tL6mKFxl8xarEzGEuWlxF-bpZ7-vwugi39blWa43zxDnOsevTPZmZtLMatL0nqpbWiew4rIlnWZCLH__uIcgA/embed"></iframe>
<!--kg-card-end: html-->
<h4 id="create-a-tinybird-account">Create a Tinybird account</h4><p>Tinybird is the <a href="https://www.tinybird.co/blog-posts/real-time-data-platforms">real-time data platform</a> that underpins our real-time dashboard. If you’re new to Tinybird, <a href="https://cloud.tinybird.co/signup?from=https%3A%2F%2Fwww.tinybird.co%2Fblog-posts%2Freal-time-dashboard-step-by-step" rel="noreferrer">create a free account here</a>. </p><h4 id="install-the-tinybird-cli">Install the Tinybird CLI</h4><p>The Tinybird CLI is a command-line tool that allows you to interact with Tinybird’s API. You will use it to create and manage the data project resources that underpin your real-time dashboard.</p><p>Run the following commands to install the Tinybird CLI (from the <code>tinybird/</code> directory) and log in:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJ4AAAAAAAAAABBKUqGk9nLKwXLjlBdNeyFXyRJ7bBbARVwWFRKgB85NDItFqtYF3ekT7op06YAZAEZ4CGKKBt-Ydg6a-apKhdxy4mTLJL1KrQ5B7OwIKi3JGQy0J9XfsM-P8EDhf0jDzO287JQeeH98yRz77NxwX0pbP__0TsAAA/embed"></iframe>
<!--kg-card-end: html-->
<p>When you log in, you'll be prompted to select a region to host your data project. This will open a browser window for you to sign in and either select an existing workspace or create a new one in that region. Once you've completed that step, your CLI will be authenticated, and your auth details will be saved in a <code>.tinyb</code> file in the current working directory. You can logout at any time with <code>tb logout</code>.</p><h4 id="start-the-tinybird-local-container">Start the Tinybird local container</h4><p>You can <a href="https://www.tinybird.co/docs/forward/get-started/install-tinybird/local" rel="noreferrer">run Tinybird locally on your machine</a> to test and validate your build before deployment. You'll need a container runtime like Docker Desktop or Orbstack, then start the local container:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJZAAAAAAAAAABBKUqGk9nLKvRidQ2JLt88MZYOiQ_4aroAe1biNQWtn9mCMPgMXx16DdeTNE-8o9RE1mQv852Z6Ag5Ttt2vsW6P5mZ5G1DtIS-YFUxN6o890__-78AAA/embed"></iframe>
<!--kg-card-end: html-->
<h3 id="step-1-create-a-data-source-to-store-events">Step 1: Create a data source to store events</h3><p>In Tinybird, a data source is defined by a database table and, optionally, a connection to an external source of data.</p><p>In this demo, we'll simply be generating some mock data using the Tinybird CLI. If you need more info on how to get data into Tinybird, check out <a href="https://www.tinybird.co/docs/forward/get-data-in" rel="noreferrer">these docs</a>.</p><p>There are a few ways to create a data source in Tinybird:</p><p><strong>If you have an existing data file</strong></p><p>If you have sample data stored in a file (CSV, NDJSON, Parquet), you can create a data source with a matching schema with the following command:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAKBAAAAAAAAAABBKUqGk9nLKzhNh4RIcweRvMdQekRcH2qYKDfrswWh8BYC9kTbbH-cGQdrxHrUMtmaxgvIGsZB4B1ZjiiGOqiKKPbbvmwIZmLqpfsDCVj54EAMk1wMpcPHSvFYJnf9VN4txI0PeDqn5EA8OIVeZAN0XdH__qLogA/embed"></iframe>
<!--kg-card-end: html-->
<p>When you use the <code>tb create</code> command, Tinybird will go ahead and scaffold your data project, generating all the necessary directories along with a few other utilities (CI/CD workflow config files, <code>.cursorrules</code>, etc.)</p><p><strong>Using LLMs</strong></p><p>If you don't have an existing source of data, you can simply use a prompt:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAK7AAAAAAAAAABBKUqGk9nLKzhNh4RIcweRvMdQekRcH2qYKDfrswWh8BYC9kTbbH-cGQdrxHrUMtmaxgvIGsZB4B1ZjiiGOqiKKPbbvmwIZmLqpfsDCVj6kqZZ3Qqs3Smo3sSgVGT4MBrgWNIZbQFiLVs0UC9wBldkiJAGoY_ozGd3MZDLJOO5dCWvHyYRId6bE0Pg89zg8aLx00l22X__1Y7AAA/embed"></iframe>
<!--kg-card-end: html-->
<p>If you use the LLMs feature, Tinybird will create a file with a few rows of mock data and store it in the <code>fixtures/</code> directory.</p><p><strong>Manually</strong></p><p>Alternatively, you can create a data source manually by defining a schema (or by starting with a prompt and modifying the result). Here's the <a href="https://github.com/tinybirdco/demo-user-facing-saas-dashboard-signatures/blob/main/tinybird/datasources/signatures.datasource" rel="noreferrer"><code>signatures.datasource</code></a> file we're using in this demo:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAI-AgAAAAAAAABBKUqGk9nLKv98kzhWeleCrxbj-9C0ku3UpVzedJoVaVfWgruTYDLNKZ2jz0LOkIDKqvxXTUkSotiPRIleFOODdY9N83gQ2H8fbKvUIi_xb2mwOnafAzF8CO-Wg2HWEKlUbjpKCiHTsvFd9SkYD20D2_n5MgYfkDVl34WGEBCJTYaYewmsFOJOVSW_ZE7nNWBeqTu_kE2Cx16y4D6leUCKXFKihnCuTp35QDvjbmuIZZtPFdqUmPjkj2EpeSA2jnSwo__3LG-k5LZ4Q_2kz0iRUeClLD9ruu7BApHvYELm18cuoK29_1NWHqDB-qbt1Vq2H0rOr8_fh1WamObL0TK0pPIt40grGhCX8-_WIQ1_vYGFfKbb2jLYdrF__VS4tw/embed"></iframe>
<!--kg-card-end: html-->
<p>Note that this demo also includes an <a href="https://github.com/tinybirdco/demo-user-facing-saas-dashboard-signatures/blob/main/tinybird/datasources/accounts.datasource" rel="noreferrer"><code>accounts.datasource</code></a>, so if you're following along, go ahead and download that too.</p><h3 id="step-2-create-api-endpoints-with-tinybird-pipes">Step 2. Create API endpoints with Tinybird pipes</h3><p>The next step is to build real-time metrics using <a href="https://www.tinybird.co/docs/concepts/pipes.html">Tinybird pipes</a>.</p><p>In Tinybird, a pipe is a set of chained, composable nodes of SQL that process, transform, and enrich data in your data sources. Pipes can be deployed as API endpoints, materialized views, or copies.</p><p>Here's why you’ll love Pipes:</p><ul><li><strong>Performance:</strong> Pipes process new data in real time, allowing for rapid transformations on streaming data sets.</li><li><strong>Flexibility:</strong> Pipes let you define custom data processing flows using filters, aggregations, and joins, enabling complex analytics and insights.</li><li><strong>Scalability:</strong> Pipes can handle massive volumes of data, scaling with your needs.</li><li><strong>Ease of Use: </strong>Pipes break up larger SQL queries into manageable nodes, which makes it easier to prototype, debug, and identify performance bottlenecks.</li><li><strong>Maintainability:</strong> Pipes organize the data workflow intuitively, making it easier to understand and modify.</li></ul><p>Creating a pipe is much like generating a datasource:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAK9AAAAAAAAAABBKUqGk9nLKwjyPxOcXOBayajOKPi814VinCdHGDY_t4KjWCbQyWXktW-DWTDhrCnyJn2vDx7YllTHHox5SDOTXwt1CngmE7YfTJ3M6L8vIfHRw-G-H163acL47niTkCvUiOC6QxO5Dqj-9i5umWjCXObIaDlb1P1f-8PUT_GhtlCnAM28hl1hI077h67w1qNd3lgjAE0eCCFF6___nM0AAA/embed"></iframe>
<!--kg-card-end: html-->
<p>Of course, you can modify the .pipe file as needed, or generate it manually. </p><h4 id="making-your-queries-more-dynamic">Making your queries more dynamic</h4><p>Depending on the pipe file you generated, your SQL may use static date range filters, but as I described earlier, real-time dashboards should be interactive. Instead of hardcoding the date range, we want our users to be able to select a dynamic range and have the results refresh in real time.</p><p>You can do this in Tinybird with its <a href="https://www.tinybird.co/docs/query/query-parameters" rel="noreferrer">templating language</a>, which lets you define dynamic, typed query parameters as well as add custom logic such as <code>if/else</code> statements and more.</p><p>Take a look at the SQL below using the Tinybird templating language. Note a couple of things:</p><ol><li>The <code>if defined</code> statement. This clause tells the pipe to execute the statements only if a certain parameter is passed. In this case, I’ve created logic such that if a boolean tag called <code>completed</code> is passed, the pipe calculates the number of completed signatures. Otherwise, it calculates all signatures.</li><li>Added <code>date_from</code> and <code>date_to</code> query parameters (<code>Date</code> type), which will dynamically change the filter based on the date values passed.</li></ol><figure class="kg-card kg-embed-card"><iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJjAgAAAAAAAABBKUqGk9nLKwwArkxUKI5k5uW7ZOOFovUC8KMBiT0G33otturml8S16XeNfpt1RivicGzBkjkjluicrPX2ylIR9x2lEvdcfcjQvcLrvcmm5dFKJqMcra-Oi7gXdzkdNMPKApFhWGyBmQlXkwKh-Bw2GaYcjLS36PjwlRw_St09cBWaI-osfTrsax215i8dYs93HB7p44rTW8UgaCUQJMV3SqYWd_sdHkDFFy3t5cMI-CKZlguzFmRy_ldzN3vh3ulvhU5Tg6JR8rXGQ8JxyEWFPTQ8fyNZsIvBAkZ7V8dlZO4kG3SzLmxsYLFNCcESo55ABMrq6siGRjZPNvagcIaOqVG8EatWUVhp9SZIwEPN5k3dTZSgMtwIgbFYGdo3PPsnJLU6-HSXwzqtk_QaQPxlssrJJAvuR_u5TmMJiFWPRpow2qfj0EBY3LTplz1doYl0v4IxfBHgVGN0rL61zrH3trli2XifV5iysozyYF8g5SLBNv-9iIcA/embed"></iframe></figure><p>Here's the final endpoint pipe we'll use in this demo:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAIzBAAAAAAAAABBKUqGk9nLKzhfO88qBhapfj_xzxnGl6fRYdcwcEf1eTpTCvj2F1N60XqdZ8MyppbKwdhsQ7m4_7emd4yONxNnS6T3_zlrtlINi7_SMYPIaQPlUVaYyyIBbYzHSizq_ncTywSp1G5G8stISJz6Op7a6is0cy4iDlx7KdLhVlgHzulmomkKw9M37mZhYoTPfdrKLwhPKK3oK5U8jmsuBYXUl6ILxV5SRSdXGpeYp4uTRgCgGFEBvLXH5Bxn3dTT5C_j_WLyvPD-6RReF2a3f_FMfi_sWEwszHCcVL0KlERjYpQEoRp1AnkvVcciyRaMrHuo4cjMd4RyLHPzhIxN_cRHepeQ9hKAASk0-dxD2jxOBViYnZP9HAZy8uWEpgessYEcsAt3RyOboaTWdoJrjqxIwv5RF2GdXkDdRIQc4Osb9I_r91JfeV-FJjZkHKPd_XbYa6Mnas7P63AnmcQyO53wdv5m3bcTXNXL9p3wtceYLu2HHqQjKZqt5EVg5CDodIk7-JvGcJUO1LyDwIK95LlGng9gKESZ-PSRpXKnlBfQPCZUmoEgPEYpmcEmyapkIl5dWVCPQLsmF-7VorPUNSPY53Z-odUStP_4jDc0TQKJoN1hCAwalN-g3x3FUFjVwsjU-PzKk5-xQdYgwopomU9yCLDvPZdKwC3C9ZkZHg7qNXe4Y-FABlX4ElsVLmlH0f6VWuuJ_kL6lg/embed"></iframe>
<!--kg-card-end: html-->
<p>This is a 2-node pipe that gets the top &lt;<code>limit</code>&gt; organizations by signatures within a date range, either completed or total depending on whether you pass a <code>completed</code> query parameter.</p><p>Make sure to name this pipe file <code>ranking_of_top_organizations_creating_signatures.pipe</code> if you're following along.</p><h3 id="step-3-test-the-build-locally">Step 3: Test the build locally</h3><p>At this point, your Tinybird project should look something like this:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAKbAQAAAAAAAABBKUqGk9nLKvRfmV1V1ADDbIwzw5DWn77T_pYfoRMvXlMsk3PIJx0KcxKMJ-YXeWNWq3bOTvNdD7Mbg4zbcENu2qFwaOrpaPeO0oaIgq1D3w20JE-nK0idJNmHyx6_l42Liu4pRVchBCpzjesYP5eLo1XvtDeQi8Fy9Esl2hfj-3SGAayOYrUMM5hcxfItSQTTf649ddae6G_0gsIT5duXCM_ur7qlqVo_-yXNOx9L5NFaUh71ZNce5fqnzD6ooag7VL29CYUs5KRWkW4J1XRrk92KPQsbUHK-0Lcji9AnUTDy8KpANS7fvzHG1ngpLY63Y6JP__9bQ_sA/embed"></iframe>
<!--kg-card-end: html-->
<p>You can build the project on Tinybird local for testing. There are two ways to do this:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJKAAAAAAAAAABBKUqGk9nLKuYM4Ag2S0r0sPBa21Y1AAbnhM4M4TJ2o_96jdXzxsr3dsfVpy6NdbpbdO8sK00MfoBodrXXwo8_jkqKugenF3___LSwAA/embed"></iframe>
<!--kg-card-end: html-->
<p><code>tb build</code> is a stateless command that checks syntax validity on your .datasource and .pipe files. You can optionally run <code>tb build --watch</code> (or its alias <code>tb dev</code>), which will build the files and watch for changes. This is great when you're actively building and want to validate changes.</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJVAAAAAAAAAABBKUqGk9nLKvXq1zJFHlZRqNcIrh8CFC2dZvsqsnkNw3bJ-qRYN_no-XKJtT-m_1YIMB7r9iqACu4-39_Cma35pW6X_CgK54alKKFQ8Vx6CP___UVQAA/embed"></iframe>
<!--kg-card-end: html-->
<p>This command deploys your project to the Tinybird local container. This can be good for testing when you don't want to maintain a stateless dev server in your terminal, but keep in mind that if you make changes to your files, you'll have to redeploy.</p><h3 id="step-4-optional-generate-more-mock-data-for-local-testing">Step 4 (Optional): Generate more mock data for local testing</h3><p>If you’re building a real-time data dashboard using existing data streams, then you won’t necessarily need to follow this step. But since I’m building a dashboard for a hypothetical document signature SaaS, I’ll need some mock data to work with!</p><p>In the past, you used to have to build a custom datagen script to get realistic(ish) mock data. In fact, there's a <a href="https://github.com/tinybirdco/demo-user-facing-saas-dashboard-signatures/tree/main/datagen" rel="noreferrer">legacy datagen script in this repo</a>. But we don't need that anymore. Now, you can just use <code>tb mock</code>. </p><p>To generate some fake data for local testing (on top of the small set of data generated during <code>create</code>), run the following:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAALWAQAAAAAAAABBKUqGk9nLKvqvlFZVBqpy_ATHwm5bBwijXtWZZndmleZMtE47im7I7IOVo1BIJ0LLlb6sd8RI82Y19tXIOzklIASzzqrwe-SnWwwD9DrLrmDKXAdrgGrXbo-pny48A2BcHOpoLhXPaBc4wN19gtjCJAvUud-5TKJVQRSUpurQdGLsT3fyRroVYwZuF0bbufs7mlwPE9lpqHDX_xDvdCIU4s3Tbgah62igXExR9L65PBQHREWiyuQjZdt_tlJMZtNMcRTgHGlh30ydCXbf2PE-xef6XiF7TyWYXGRf4AstqpX7325DoN7N8K2mpwsfAqnH7PyI_lbHux5IABgfcnXpIa8enOLLO6gCi5Nsumn1lzQ1eqxnTuqnZ3sWNOgPaCdMrA2VLpMJ_udwycgCMr4qVxynq8rqDFhF_25ZGA5YzrLv_5ox9cA/embed"></iframe>
<!--kg-card-end: html-->
<p>This generates mock data that matches the schema for each data source. You can specify the number of rows to generate, and add a prompt for more specific instructions.</p><p>This command generates both an NDJSON fixture file and an SQL file (which is used to generate the mock data on the local database). You can make tweaks to the SQL file to generate a more precise mock data fixture.</p><p>Note that if you run <code>tb mock</code> inside of the <code>tb dev</code> console, the project will automatically rebuild. However if you're testing on a local deployment you may need to redeploy to local after you generate a new fixture.</p><p>To verify that your local data sources have mock data, you can query them:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJ1AAAAAAAAAABBKUqGk9nLKvRT7pU5WCZGRVz4_HIYMqQnrbrrD6JhqCb32fpvQfTcZWIZEblsbBWBUcD36VKTi5ZEz3Ui1jw-tWetogx-XSmqM_Gn6dhljc3UCaU2TK4yx3L6O7JGvPempO5vo6_A0ydfdgKsCdkGI___cuQAAA/embed"></iframe>
<!--kg-card-end: html-->
<p>This will return the current number of rows in the <code>signatures</code> Data Source. If you’re mock data creation is working (and still running in the background), you’ll see that number tick up.</p>
<!--kg-card-begin: html-->
<div class="tip-box"><div class="tip-box-container"><div class="tip-box-title">Reminder</div><div class="tip-box-content">This project is using mock data to simulate data generated by a hypothetical document signatures app. If you have your own app that’s generating data, you don’t need to do this! You can just call the Tinybird Events API from your frontend/backend code to send data directly from your app to Tinybird.</div></div></div>
<!--kg-card-end: html-->
<h3 id="step-5-test-your-apis">Step 5: Test your APIs</h3><p>Your real-time dashboard will need an API (Application Programming Interface) to ensure seamless integration, accessibility, and interaction with other applications or services.</p><p>Here's why real-time APIs are so important for fast dashboards with many users:</p><ol><li><strong>Integration &amp; Interoperability:</strong> APIs allow your dashboard to be accessed programmatically by other applications. This enables a more comprehensive integration with different platforms, tools, or third-party services.</li><li><strong>Scalability:</strong> Through APIs, the dashboard can be quickly and easily scaled to serve multiple clients, including web, mobile, or IoT devices. This ensures that as your needs grow, your architecture can adapt without major redesigns.</li><li><strong>Real-Time Data Access:</strong> If your dashboard relies on real-time or frequently updated data, APIs are essential for providing up-to-the-minute access to the information, enhancing decision-making and user experience.</li></ol><p>With Tinybird, your endpoint pipes are automatically deployed as low-latency, high-concurrency REST APIs. You can secure your APIs with <a href="https://www.tinybird.co/docs/forward/get-started/administration/auth-tokens" rel="noreferrer">static tokens or JWTs</a>.</p><p>Now let’s test your new API.</p><p>You can get the URL of your local API:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAKOAAAAAAAAAABBKUqGk9nLKveDjFl9fKvpZYnd1YPDMwfXPicY3ioDpgx0ZcDqcCQ7_EwWJOwclEavG8kxp6AThfxUw7EZHmbCoLlBLNC568-PV_u5Zs3QnW3cVeoMNk6f3rKAylvbpGxQV3U66MuQafXdZxJlpyXqUtKFeJO4ZorTQn__w0-QAA/embed"></iframe>
<!--kg-card-end: html-->
<p>That should give you something that looks like this:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAALSAAAAAAAAAABBKUqGk9nLKveDjFl9fKvpZYnd1YPDMwfXPicY3ioDpgx0ZcDqcCQ7_EwWJOwclEavG8kxp6AThfxUw7EZHmbCoMZZTeBqOSC9Sub-6J3KbDl-aIFuFEfPo0H3t4gJOylUtLhk9dPq-SIflYFMEKUdrWh0zatpUkB-jhb4v3y5RABqgpWAKhH_YZGvXRH4P6ZzirhG6YaZpebm3G0DZFp7bacPv4pE6PEpYQojzZW0x0KC7NorEbX_rttAAA/embed"></iframe>
<!--kg-card-end: html-->
<p>Within the endpoint URL, you will notice the <code>date_from</code> and <code>date_to</code> parameters. These control the date range for the query, and they can be modified to filter the results accordingly. You’ll also see the <code>limit</code> parameter, which controls how many rows are returned.</p><p>Fetch that with a <code>curl</code> or in your browser to get the results of your API deployed locally</p><p>Try altering the values for these parameters in the browser's address bar. As you change the dates or limit and refresh the page, you should see different data returned in response to your query. This behavior verifies that the dynamic filtering is working correctly, allowing the query to adapt to different user inputs or requirements.</p><p>If you request the data in a JSON format, you’ll also receive some metadata about the response, including statistics about the query latency:</p><figure class="kg-card kg-embed-card"><iframe width="100%" src="https://snippets.tinybird.co/XQAAAAKyAAAAAAAAAABBKUqGk9nLKwMdmjmvHOYW1P_tRfLfKSpRWor_5sDQgC4Gocsc-zf9bLlhAudOi6CFM_s_rItP4nBubj53fdZ8XKSMGnJu3VzIlg-uNlVdGv5kQR6PlJUsse3-5VIMR7383srSjBVGIP16MptnI8_VBELprrnjrL34nyU3TtPnysybKdBPpfupfUA6PZVQpCXab__8VswA/embed"></iframe></figure><p>In the above example, the API response took barely 1 millisecond, which is a recipe for fast dashboards! You can utilize this metadata to continue to monitor your dashboard query performance and optimize as needed.</p><h3 id="step-6-create-real-time-dashboard-components-with-tremor-and-nextjs">Step 6: Create real-time dashboard components with Tremor and Next.js</h3><p>Now that you have a low-latency API with real-time dashboard metrics, let’s create the visualization layer using Next.js and Tremor. These two tools give us a scalable and responsive interface that can effectively integrate with Tinybird's APIs to display data dynamically.</p><p>Here's how you can get started:</p><h4 id="add-tremor-to-your-nextjs-app">Add Tremor to your Next.js app</h4><p>We’re going to use Tremor to create a simple bar chart that displays the signature count for each organization. Tremor gives you beautiful React chart components that you can deploy easily and customize as needed.</p><p>Start by installing Tremor with the CLI:</p><figure class="kg-card kg-embed-card"><iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJmAAAAAAAAAABBKUqGk9nLKvRZUIuu24V3ECC7G9JMU-JkGcHlHPDiO15X9E7nyvSD26t8yK7FJ_FgIF4wRX9rQ-XMKYarwNZNV5HfR0UhRnMl5f-RbsmUv48OVpEad76pKPn6ABAENPX__9mxAAA/embed"></iframe></figure><p>Select <code>Next</code> as your framework and allow Tremor to overwrite your existing <code>tailwind.config.js</code>.</p><h4 id="set-up-environment-variables">Set up environment variables</h4><p>Next, you need to add your Tinybird host and admin token as environment variables so you can run the project locally. Add the following to your <code>.env.local</code> file:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAALRAAAAAAAAAABBKUqGk9nLKwkAtFCkm2IEjkdEIJl629Gu2HPLnqlAB8S2fVm_hPvMCqqJWlFUE8uLKN-anfYa9VjislK2RiXsiEzBV2pu9Qgvy3jTF-AI3EdXj_c2yNVryZaphFSZce2cT7aqE4bYQxsYROJ9Nv3VjZaObH-TI7ioKmD-aZsOsohpcAuJSvHl5dOCByi-vZxIMtPOSQqSLHgmPExIq64eSzTSEgvdbD__-tl4AA/embed"></iframe>
<!--kg-card-end: html-->
<p>Note, you'll need to have a read token for your API if you don't already. To create one just run:</p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAALWAAAAAAAAAABBKUqGk9nLKvxCDxOcXOBayajVf6kLaMps1Ry9qsVVhXxQK9xyN6I74DaKXz30omX7MM-S0ZA1kEjJYunUPfc2XSxODe9kg6CBJuoHayZGOKUv_y8L2cYEF91hUOjuQA8kW3YoiH7H9G0oOYkC3OarzZdnjX12uPRYRYq8twOJdGAXE4zwLjrl6xUZFXtWiI1haQCI2lC4j_mFxT9oZ267oJHbtQEcQeSFBQH-428A/embed"></iframe>
<!--kg-card-end: html-->
<h4 id="set-up-your-indexjs">Set up your index.js</h4><p>Let’s create an index page to build and display our real-time dashboard. Note that you could very easily "vibe code" this with an AI-assisted IDE, but for the sake of completeness I'll walk you through it.</p><figure class="kg-card kg-embed-card"><iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJzAAAAAAAAAABBKUqGk9nLKwp6RYmC6VCBhEQed_5Evw6AHIVLr3Gk1vOoJMQ-C_67HRUhOjUlI0RYmSG3ma0-1J0lbam3x-rvOiFI4hjLhUL3c2Lv6X3_nnxFYL1rFAavsklbSqX-K9HOQJFi9NMr-lMAqIEMHvqeTIA/embed"></iframe></figure><p>Next.js may have made a default <code>index.js</code>, in which case, start by clearing its contents.</p><h4 id="import-ui-libraries">Import UI libraries</h4><p>To build your dashboard component, you will need to import various UI elements and functionalities from the libraries provided. Make sure you have the following libraries and components imported at the beginning of your file:</p><figure class="kg-card kg-embed-card"><iframe width="100%" src="https://snippets.tinybird.co/XQAAAALeAAAAAAAAAABBKUqGk9nLKwKopwLBEiptpR7DuPtveXlWG39v4EkQBcX6pX3-szud9qowX0JjeL_7s0BNQal4xRJ20BiT3hVVfKGz5No23ZbGUH15Eopd0MtSbWYEHnzGZCu7DonQnQpJWsu9AK-7RLlKgGecJzEOp-nsH4SLnc1s9zRUE8bmldTIuBxWPDVdioWjCwStsjV7n1VzX7Qt6dWV1CJ_cb2eoz0ixd7A051aVCLxvtnagk-GI__9LrBg/embed"></iframe></figure>
<!--kg-card-begin: html-->
<div class="tip-box"><div class="tip-box-container"><div class="tip-box-title">Use Client</div><div class="tip-box-content">Note we’re using the `use client;` directive to render the components on the client side. For more details on this, check out the <a href="https://nextjs.org/docs/app/building-your-application/rendering#network-boundary" target="_blank">Next.js docs</a>.</div></div></div>
<!--kg-card-end: html-->
<h4 id="define-constants-and-states">Define constants and states</h4><p>Inside your main component, define the constants and states required for this specific component. We’re going to set the state for the chart data and the latency of the query (so we can see how fast this dashboard is!):</p><figure class="kg-card kg-embed-card"><iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJJAwAAAAAAAABBKUqGk9nLKzhQ5J7vHatFcB73SAzbqc9qQDE_DRC-cXaC0pPvcogkTL3R63oGJEcCuYDXuV3SF30ksm_jA1__IQZxe6OkVS0rguladZj9Sex-VjcWGvp0XNAmjJP3l3uq8i1xztqpqfIT8_e6yMv1IPYso8k_km6Dq6Y07E-JaZDNP23huQd92uE3hg9Toclgo298MspJG53S6q-SlnYDzWMx2ppSFL9gnHgOEmD1u7jC03JVo0NrU6rxmH5fxy9JZPsWZjaK5IMg8rEAjvdzSAQ4jba4rV9AXe_fBzCoOTZ7BK51nU5cn-dZ6Z487Exiik8ZPt7kxqW6X7PZnY5N1bDNbcTbw0RqZPM8M2npC6dJB1yevdfHyn0D4l7fTNgzT2Z7SR6yrphAHUqcRlBUhfTJqhz6yQEmDFOVIH-AOJTFABNpsQ1IXGu9mNG7ZViLl7PSfR_A1uSRA_RKfXE7FMeH-MNvNdFJNYGVUjrnlrODYyZoGGPZ9oRRuBG9zVbJWTBJk5NiQwPWR7bCO4JOZsTTXRG5A3C4I4GCnUFh18K0WrgKOSTVC4VRg5k2YevlpxjWYy_OIANwgqVZ7HFZbbMMITlvq8s-1pHppEGsarZgW7Id4fKlSuhR7jBRv5SW4QOw6yM4lXxtkM-9YnL_N0Q0AA/embed"></iframe></figure><h4 id="connect-your-dashboard-to-your-tinybird-api">Connect your dashboard to your Tinybird API</h4><p>You’ll need to write a function to fetch data from Tinybird. Note that for the sake of brevity, we are hardcoding the dates and using the default limit in the Tinybird API. You could set up a <a href="https://www.tremor.so/docs/ui/date-range-picker" rel="noreferrer">Tremor datepicker</a> and/or <a href="https://www.tremor.so/docs/components/number-input">number input</a> if you wanted to dynamically update the dashboard components from within the UI.</p><figure class="kg-card kg-embed-card"><iframe width="100%" src="https://snippets.tinybird.co/XQAAAAKqBQAAAAAAAABBKUqGk9nLKzhXogI7uOq58F8sUESvC13JVcoNO3TC90Dd8KWMF12PJf0uO0zjJMAkZvRhwRc8A_X4xlG-uQbpjThWoXAxeCN6-dWBN4SmuDjf3Ct_BpyXUJjyLSP5tdOtxIBXUNlondYmymZSz1j3J7MRLzvViw80zvd5El0_7W6xn5tHkHo7Xg2qG9ezjvAK2mMEFpSG9DuqExkuf2juCwmRQV-JJiiGWuUH6n5d0WUC6kENEOeY1zc6i0YzqRXwB7tfAG0AhwHWal9FyNlhhqoZwnTf9dQmLsk6oEB0c21DYNAvmjn6TjLtZrWOeNneD-o8jrAWnw9vmOBlh5yOb7eZL4_fqxdb6gG-BH65pAbe0luWYdgnEGWVTjUaALlcYg0tYY0CGHCably2C6a8V-pe32QfnTZtYITXybnXIeFoacUA1vNsoFk_PUApwJWEtQS_QzXh_LGqIFLv4jIt9YfQr8PJqsd7SUdjIjs4UceJz5XQtA9bQF9QGufB-ks2swJUKvQf0__lmnOxxL9B_Mu9JBiGyBxvvvZdIld644Z7qFSKnSEePqvbYJxP1Yp1Q-9rLpgQTTXFiZTOfadZVIDX-KRov85UkN8QWgpWXG2CS-pyFx-5oSaCgST9-D6uhkf8n8fC5AUjSlfM-H5g4xD5F-GgSpxbZYpC4uRE6H5ns1lDcgBR1Cf-J9hR5QuBxUzietnRpiPl4t238TbvDHLOqAFrPGKRfGoYzDIuuew2bT2rhjh-kHDCtrEZVHcUTThuLygNIV3mnZkEJVOu-6dhUzDnk0-Pcymqx2MNWOADxBAOPJW5Y4PEWa-HrCIVF_fS2vzSaJFu8Y9SWqqcnDgmkON7BEvtYOUgdRh5KQkqqdCENG_m2dX9Y9QthM-KOborhvmRP1n46OKePfVv_gcWMLsWCxzCzP60oGO-5bCCLUmLk47mdJdMdV-t_4Lsyhw/embed"></iframe></figure><h4 id="configure-the-tinybird-api-call">Configure the Tinybird API Call</h4><p>You need to define the specific URL for the Tinybird API call and make the fetch request using the <code>fetchTinybirdUrl</code> function inside the <code>useEffect</code> hook:</p><figure class="kg-card kg-embed-card"><iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJhAgAAAAAAAABBKUqGk9nLKzha_5tbFnwnrKjThvHCqfq8xzwlrz_5pYYcb4a8P4czPPnuodXE__gYSqZlP9i2wdfCmTdZpYzFMKeK3a7Lf3bm8u_8yi0a_mcAdBfoddIPc0KghIUOQLTWCQw1_HkMEAYki6DqyBySpWX2hGsz7TpUmdJcoqbBwH9Dg7570V1prEHxV4wR2x-hS7nTcpESny5sHmBUDcfkgAwYV1WMg-pLKYv2fHPYnEyoTgNYaXuSQZrM701WLwlusMsENp-i8rhtnNXFLQMcQkguW7hti52Y8q1v6lEkqS0pIyQ7Lth-7SdXuBQ1sSwPbC8fIU1vb5ioUKOgNj78sFQvMu3np93MIX_0a8Skv--_6l1y16WuWwcvMqSp_-lXdeVPnGN5l0PBtNhe4v8CNSIQY9HsO-LF51BiJBqcGP8Hw6wGGpXaSDjNNM8I9iw4V27kK0WJ7izrMNCJTBmPBtDQgPpwf__ttbva/embed"></iframe></figure><h4 id="render-the-component">Render the Component</h4><p>Finally, include the rendering code to display the "Ranking of the top organizations creating signatures" in the component's return statement:</p><figure class="kg-card kg-embed-card"><iframe width="100%" src="https://snippets.tinybird.co/XQAAAAKlAgAAAAAAAABBKUqGk9nLKw87eObdLgTqkQLcxrHkvjVWpYCRAQ17l5lMvttxPLCtvMMmDb-kTgqrJVUbyhCO27vLXf6dDjjzv7EfMPG3P90IrYFFHmphrYdyaIbWOzbtGuHBpHTe695hlVmWbmwm7dngdV_kaS85_zMDIO-YNtLBVvn9CdMyYn0zOYpF4cZ0tn5UCsABrul-dbbQ-OBVu0fp1oWC3fFVfFYd4jHherj0mavEDBSiyq4PeAhp1MbRlNuDpY2z5l3GUDr0UOEjI04PiqMvKQfcdMMpO6nCzqItJZNaILZpqD2BSiq_K52pMAiE-q1GpLabmQyuYGXMEGaugvO_5FPJw6zlNDHRCG9TEeoXn3pe91l6I6ZXi2MVJQoJn4TmkSsHeWOLonqbRUiKR6f9PpSuPtluFZ30oRsDdgOA3lhFuX7uLOT4sWCBQpeRFp7ulduC09aZbPSYikXyzb-38Hkm-z59nczGxaNcrTb6I32B2NwN_5_GbEA/embed"></iframe></figure><p>To view your real-time dashboard component, run the following:</p><figure class="kg-card kg-embed-card"><iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJbAAAAAAAAAABBKUqGk9nLKvRhdt7jwU0BO7-jo5YmrmXP95NHxwkAEidOTOER9mmwsKD-ochEVM96Dr8kw2mNXVOJsQLOq4_LRekG_lci3pLAg8wCoZQ3nc1ISo6Mjjc5ygf-hv8A/embed"></iframe></figure><p>Navigate to <code>​​</code><a href="http://localhost:3000/"><code>http://localhost:3000/</code></a> in your browser. You should see something like this:</p><figure class="kg-card kg-image-card"><img src="https://tinybird-blog.ghost.io/content/images/2023/09/64f23980aff4ad96e1677a9f_VJ59z6ms3N9DzysGJXON7VJCo1f5sdXgiUSXNMWfFPb2RkQzFb_m10Ofcfeu5A6CKvHJISACjin-80zPEYH6c-WnW6obm3jKdabrmLGzEIz3G_Df65nKKz8jdZCw_3zaDk-lyQKM9nHa3-ssFVJJ4qo-7.png" class="kg-image" alt="A screenshot showing the final rendered real-time dashboard component" loading="lazy" width="1600" height="1003" srcset="https://tinybird-blog.ghost.io/content/images/size/w600/2023/09/64f23980aff4ad96e1677a9f_VJ59z6ms3N9DzysGJXON7VJCo1f5sdXgiUSXNMWfFPb2RkQzFb_m10Ofcfeu5A6CKvHJISACjin-80zPEYH6c-WnW6obm3jKdabrmLGzEIz3G_Df65nKKz8jdZCw_3zaDk-lyQKM9nHa3-ssFVJJ4qo-7.png 600w, https://tinybird-blog.ghost.io/content/images/size/w1000/2023/09/64f23980aff4ad96e1677a9f_VJ59z6ms3N9DzysGJXON7VJCo1f5sdXgiUSXNMWfFPb2RkQzFb_m10Ofcfeu5A6CKvHJISACjin-80zPEYH6c-WnW6obm3jKdabrmLGzEIz3G_Df65nKKz8jdZCw_3zaDk-lyQKM9nHa3-ssFVJJ4qo-7.png 1000w, https://tinybird-blog.ghost.io/content/images/2023/09/64f23980aff4ad96e1677a9f_VJ59z6ms3N9DzysGJXON7VJCo1f5sdXgiUSXNMWfFPb2RkQzFb_m10Ofcfeu5A6CKvHJISACjin-80zPEYH6c-WnW6obm3jKdabrmLGzEIz3G_Df65nKKz8jdZCw_3zaDk-lyQKM9nHa3-ssFVJJ4qo-7.png 1600w" sizes="(min-width: 720px) 720px"></figure><p>And that’s it! You’ve created a real-time dashboard component using Tinybird, Tremor, and Next.js. You’ll notice the dashboard is rendering very quickly by taking a peek at the latency number below the component. In my case, Tinybird returned the data for my dashboard in a little over 40 milliseconds aggregating over about a million rows. Not too bad for a relatively unoptimized query!</p><h3 id="step-7-deploy-it">Step 7: Deploy it</h3><p>Once you've validated your local build, you can deploy it to the cloud.</p><p>To deploy your Tinybird data project, you have two options:</p><p><strong>Manually:</strong></p>
<!--kg-card-begin: html-->
<iframe width="100%" src="https://snippets.tinybird.co/XQAAAAJhAAAAAAAAAABBKUqGk9nLKvxC6zJFHlZRqNcZfp6UrtbZn33XG_xTFMtA_br7tVjhBboge-NqNNLjKb5VdOY3T0qgzbqUmpOgMYi3xhGcgd-j78q6F_hul6pj7gBy2--eNqsf_zx8AAA/embed"></iframe>
<!--kg-card-end: html-->
<p><strong>Or in CI/CD:</strong></p><p>Tinybird automatically generates CI/CD config files for GitHub and GitLab. If you want to run deployments as a part of your git workflow (so you can deploy Tinybird alongside your app), you just need to add your Tinybird host and token as secrets in your git provider and modify the .yaml files as needed for your workflows. <a href="https://www.tinybird.co/docs/forward/test-and-deploy/deployments/cicd" rel="noreferrer">More info on deploying Tinybird in CI/CD</a>.</p><h2 id="next-steps">Next Steps</h2><p>This tutorial showed you how to build a single real-time dashboard component, but you probably want to add additional components and interactive elements.</p><p>If you need ideas, check out the <a href="https://github.com/tinybirdco/signatures-dashboard">GitHub repository for this project</a>.</p><p>You can also spend some time optimizing your data project for faster responses and minimal data processing using fine-tuned indexes, Materialized Views, and more. For tips on <a href="https://www.tinybird.co/docs/guides/best-practices-for-faster-sql.html">optimizing SQL queries</a> or <a href="https://www.tinybird.co/docs/forward/work-with-data/materialized-views" rel="noreferrer">building Materialized Views</a>, check out the <a href="https://www.tinybird.co/docs">Tinybird docs</a>.</p><h2 id="wrapping-up">Wrapping up</h2><p>If you are interested in building a web based dashboard or any other real-time visualizations, you need a data stack and frontend library that can keep pace. In this tutorial, you learned how to use modern tooling to build an end-to-end real-time data pipeline and dashboard. With Tinybird, Tremor, and Next.js, it's possible to build a real-time analytics dashboard from scratch in less than an hour.</p><p>The combination of Tinybird, Next.js, and Tremor provides a powerful solution for building real-time dashboards, but the real “speed layer” here is Tinybird. Here’s what Tinybird is perfect for building real-time data visualizations:</p><ul><li><strong>Real-Time Data Ingestion and Processing</strong>: Tinybird can handle large streams of data in real time. Unlike traditional batch ETL processes, it can ingest, process, and analyze millions of events per second on the fly. This means that your dashboard can reflect changes almost instantly, keeping the insights fresh and timely.</li><li><strong>Highly Optimized Query Engine</strong>: Tinybird’s query engine is built to execute complex analytical queries in milliseconds. It can handle filtering, aggregating, or joining data without breaking a sweat, which means your dashboards won’t experience lagging refresh times.</li><li><strong>Scalable Architecture</strong>: Tinybird is a scalable, serverless real-time data platform. It flexibly scales storage and compute resources based on demand. As your data volumes and user loads increase, Tinybird responds to ensure fast dashboards at scale.</li><li><strong>Integration with Streaming Sources</strong>: Tinybird includes many first-class connectors for streaming data sources (like Apache Kafka, Google Pub/Sub, Amazon Kinesis, and more), so you can unify data from multiple sources directly into your visualization layer.</li><li><strong>Real-time API Publication: </strong>Tinybird is designed specifically for user-facing applications. With Tinybird, you can <a href="https://www.tinybird.co/docs/concepts/apis.html">instantly publish SQL-based metrics as APIs</a> that you can integrate into your frontend.</li><li><strong>Compatibility with Next.js and Tremor</strong>: Tinybird's architecture and API are designed to work seamlessly with modern frontend frameworks like Next.js and visualization tools like Tremor. This integration creates a smooth user experience from data ingestion to visualization.</li><li><strong>Easy to Use</strong>: Even with all its robust capabilities, Tinybird remains accessible to developers. Its simplified SQL-based query language and well-documented APIs mean that building and maintaining a real-time dashboard does not require specialized skills or extensive training.</li></ul><p>If you're dabbling in real-time data processing or looking to shift to event-driven architectures for your dashboards, <a href="https://www.tinybird.co/">Tinybird</a> could be for you. It's free to start and designed to help you build real-time data pipelines fast. You can <a href="https://www.tinybird.co/signup?referrer=https%3A%2F%2Fwww.tinybird.co%2Fblog-posts%2Freal-time-dashboard-step-by-step">sign up here</a> (no credit card required!)</p><p>Stuck somewhere along the way? Join the <a href="https://www.tinybird.co/community">Tinybird Slack community</a> for help. Want to dive deeper into Tinybird? The <a href="https://www.tinybird.co/docs">Tinybird docs</a> are a great place to start.</p><h2 id="additional-resources">Additional Resources</h2><ul><li><a href="https://www.tinybird.co/docs">Tinybird Documentation</a></li><li><a href="https://www.tinybird.co/starter-kits/web-analytics">Tinybird Web Analytics Starter Kit</a></li><li><a href="https://www.tremor.so/docs/getting-started/installation">Tremor Documentation</a></li><li><a href="https://nextjs.org/docs">Next.js Documentation</a></li></ul><h2 id="faqs">FAQs</h2><h3 id="what-are-the-technologies-used-for-creating-a-real-time-dashboard-and-why">What are the technologies used for creating a real-time dashboard, and why?</h3><p>The application uses Tinybird for real-time data ingestion and real-time analytics, Tremor for data visualization, and Next.js as a fully-featured React framework. These technologies are chosen for their efficiency in processing large streams of real-time data, visualizing it in a user-friendly way, and ensuring a smooth and visually appealing rendering.</p><h3 id="what-makes-tinybird-essential-for-real-time-data-handling-in-the-tech-stack">What makes Tinybird essential for real-time data handling in the tech stack?</h3><p>Tinybird provides real-time data ingestion and processing, optimized query execution, scalable architecture, compatibility with streaming sources, integration with modern frontend frameworks, and accessibility to developers. Its architecture is tailor-made for real-time analytics dashboards, making it an essential part of this process.</p><h3 id="how-can-i-set-up-the-tinybird-cli">How can I set up the Tinybird CLI?</h3><p>Install the Tinybird CLI with <code>curl -L tinybird.co | sh</code></p><h3 id="why-are-most-data-dashboards-slow-and-how-does-this-application-overcome-that">Why are most data dashboards slow, and how does this application overcome that?</h3><p>Traditional dashboards are slow due to issues like batch ETL processes, complex BI tools, heavy data stack, poorly designed queries, and lack of scalability. By using Tinybird, Next.js, and Tremor, this application overcomes these issues with real-time processing, optimized queries, and scalable architecture.</p><h3 id="what-are-the-primary-components-of-a-real-time-dashboard">What are the primary components of a real-time dashboard?</h3><p>A real-time dashboard consists of Data Sources, a real-time data processing engine, a real-time visualization layer, and interactive controls. They collectively provide an up-to-the-second snapshot of critical metrics and KPIs, allowing users to interact with the data and gain insights instantly.</p><h3 id="can-the-structure-described-in-the-post-be-applied-to-other-use-cases-besides-signatures">Can the structure described in the post be applied to other use cases besides signatures?</h3><p>Yes, the structure and technologies can be adapted to almost any use case that requires real-time data handling, making it highly versatile.</p><h3 id="what-precautions-should-be-taken-with-tinybird-authentication-tokens">What precautions should be taken with Tinybird authentication tokens?</h3><p>The admin token should be kept secure and not shared or published. It's essential to hide secrets by adding the Tinybird config file to the <code>.gitignore</code> file, ensuring that it won't be committed to the repository.</p>
