Skip to content

Guides

Build a dashboard app

Write a small web app with the Dashboard SDK that reads a Data Bucket, publish it to a File Storage and open it as a dashboard of the console.

Tutorial3 min read

In this tutorial you build a web app that lists the latest temperatures of a Data Bucket and updates itself when new records arrive. You publish it to a File Storage and open it as a URL dashboard inside the console. It takes about 20 minutes.

The app uses the Dashboard SDK (@thinger-io/dashboard-sdk), which needs Thinger.io 7.4 or later. The app never handles credentials: inside the console it receives the session of the person viewing it, so it can read exactly what that person can read.

What you need ​

  • A Thinger.io account. On the free Cloud account your server is iot.thinger.io; on a private instance, use your own domain wherever this tutorial says YOUR_SERVER.
  • A Data Bucket with a numeric temperature field, such as the climate bucket of From an ESP32 to a dashboard.
  • Node.js 20 or later, and the thinger CLI signed in to your account.

Create the project ​

  1. Create a Vite project

    bash
    npm create vite@latest climate-app -- --template vanilla-ts
    cd climate-app
    npm install

    Any bundler works. thinger dashboard init creates a ready-made React or Angular project instead; see Dashboard apps.

  2. Install the SDK

    bash
    npm install @thinger-io/dashboard-sdk
  3. Use relative asset URLs

    A File Storage serves the app from a folder, so build it with relative URLs:

    vite.config.ts
    ts
    import { defineConfig } from 'vite';
    
    export default defineConfig({
      base: './',
    });

Read the bucket ​

  1. Write the page

    Replace index.html with a heading, a status line and an empty table:

    index.html
    html
    <!doctype html>
    <html lang="en">
      <head>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <title>Climate</title>
      </head>
      <body>
        <h1>Climate</h1>
        <p id="status">Connecting…</p>
        <table>
          <thead><tr><th>Time</th><th>Temperature (°C)</th></tr></thead>
          <tbody id="rows"></tbody>
        </table>
        <script type="module" src="/src/main.ts"></script>
      </body>
    </html>
  2. Subscribe to the bucket

    Replace src/main.ts. The app waits for Thinger.ready(), then subscribes to the last 6 hours of the bucket: the callback runs with the records at once and again whenever the bucket is written.

    src/main.ts
    ts
    import { Thinger } from '@thinger-io/dashboard-sdk';
    import './style.css';
    
    const BUCKET = 'climate';
    
    type Reading = { ts: number; temperature: number };
    
    const status = document.querySelector<HTMLParagraphElement>('#status')!;
    const rows = document.querySelector<HTMLTableSectionElement>('#rows')!;
    
    function render(data: Reading[]) {
      rows.replaceChildren(
        ...data.map((r) => {
          const tr = document.createElement('tr');
          tr.innerHTML = `<td>${new Date(r.ts).toLocaleString()}</td><td>${r.temperature.toFixed(1)}</td>`;
          return tr;
        }),
      );
      status.textContent = `${data.length} readings from the last 6 hours`;
    }
    
    try {
      await Thinger.ready();
      Thinger.buckets.subscribe<Reading>(BUCKET, { from: '6h', items: 50 }, (data, err) => {
        if (err) {
          status.textContent = `Cannot read ${BUCKET}: ${err.message}`;
          return;
        }
        render(data ?? []);
      });
    } catch {
      status.textContent = 'Open this app from a Thinger.io dashboard.';
    }

    Replace src/style.css with your own styles, and delete the other files of the Vite example in src/. The Dashboard SDK reference lists every module (devices, buckets, properties, alarms, storages…) and the rules an app follows inside the console, such as hash routing and no localStorage.

  3. Build it

    bash
    npm run build

    The build is in dist/.

Publish it ​

The app goes to a public File Storage, and a URL dashboard points at its folder.

Console

The console shows dashboard apps but does not create URL dashboards. Go to File Storages, click Create a File Storage, create a public storage called climate_app and upload the contents of dist/ to it, keeping the assets folder. Then create the dashboard with the API, the CLI or an agent.

API

Upload each file of dist/ to a public storage named climate_app (see File Storages), then create the dashboard with source: "url" and the folder of the storage, with the trailing slash:

POST/v1/users/{user}/dashboardsAPI reference
bash
curl -X POST "https://YOUR_SERVER/v1/users/YOUR_USER/dashboards" \
  -H "Authorization: Bearer YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "dashboard": "climate_app",
    "name": "Climate app",
    "source": "url",
    "url": "/v1/users/YOUR_USER/storages/climate_app/files/"
  }'

CLI

bash
thinger dashboard publish dist --storage climate_app --to dashboard:climate_app --name "Climate app"

The command creates the public storage if it is missing, uploads dist/ and creates the dashboard pointing at it. It prints the console link of the dashboard. Add --preview to also get a temporary read-only link that opens the app outside the console. See the CLI reference.

AI

Ask your AI agent

Publish the dist folder of my climate-app project to a public storage called climate_app and create a dashboard called climate_app that shows it.

The agent uses thinger_dashboard_publish. See Build dashboards with AI.

To publish a new version, build again and run the same publish command: consoles that have the app open load the new files on their next reload.

Result ​

Go to Dashboards and open Climate app. The console shows your app in its layout, with the latest temperatures of the bucket, and a new row appears each time the bucket gets a record.

Climate app dashboard inside the console, with a table of the latest temperature and humidity readings of the climate bucket
The Climate app inside the console

If the app shows "Open this app from a Thinger.io dashboard", you opened the storage file directly, without a session: open it from Dashboards or with a preview link.

Next steps ​