Skip to main content
Web components are not supported in server-side rendering (SSR) environments. This example assumes that your Svelte application is running entirely on the browser (client-side only). If you attempt to use the chart component library within SSR frameworks (such as SvelteKit) without proper dynamic import or client-side rendering guards, you may encounter errors.

Instantiating the Nebuly Charts Client

To get started, you’ll first need to instantiate the Nebuly Charts client. The following code snippet shows how to do this by importing the library and creating a client instance using your API key:
import NebulyReports from '@nebuly-ai/reports';

const client = new NebulyReports.Client({
  publicKey: '<YOUR_NEBULY_PUBLIC_KEY>',
});

Providing the Nebuly Client via Svelte Stores

Next, it’s best practice in Svelte to provide the Nebuly client throughout your app using Svelte stores. This lets any component access the client instance via the store, without having to pass it through props manually. Here’s how you can create a store for the Nebuly client:
// stores/nebulyClient.ts
import { writable } from 'svelte/store';
import NebulyReports from '@nebuly-ai/reports';

const client = new NebulyReports.Client({
  baseUrl: "http://localhost:8080/api/external",
  publicKey: "<YOUR_NEBULY_PUBLIC_KEY>",
});

export const nebulyClient = writable(client);

Registering Nebuly Web Components in Svelte

To use Nebuly web components (such as charts) in your Svelte application, you need to register them once—usually in your main entry point file. This enables the custom HTML elements to be recognized by the browser. The snippet below demonstrates how to do this within a Svelte application setup:
// main.ts
import './app.css';
import App from './App.svelte';
import NebulyReports from '@nebuly-ai/reports/web';

NebulyReports.registerComponents();

const app = new App({
  target: document.getElementById('app'),
});

export default app;

Displaying a List of Reports

If you want to display a list of available reports fetched from the Nebuly API, you can define a simple component that leverages the client store and handles loading and error states gracefully. Here’s an example implementation:
<!-- ReportsList.svelte -->
<script lang="ts">
  import { onMount } from 'svelte';
  import { nebulyClient } from '../stores/nebulyClient';
  import { goto } from '$app/navigation';

  let reports = [];
  let isLoading = true;
  let error = null;

  const goToReport = (reportId: string) => {
    goto(`/report/${reportId}`);
  };

  onMount(async () => {
    try {
      isLoading = true;
      const client = $nebulyClient;
      reports = await client.listReports();
    } catch (err) {
      error = err;
    } finally {
      isLoading = false;
    }
  });
</script>

<div>
  {#if error}
    <div>
      Error: {error.message}
    </div>
  {:else if isLoading}
    <div>
      Loading...
    </div>
  {:else}
    {#each reports as report (report.id)}
      <button on:click={() => goToReport(report.id)}>
        {report.title}
      </button>
    {/each}
  {/if}
</div>

Rendering a Report and Its Charts

Once a user selects a report, you’ll want to fetch detailed data for that report and render the associated charts. The following example demonstrates how you might build a Report component that retrieves report data, supports loading and error handling, and renders each chart using a separate ChartComponent:
<!-- Report.svelte -->
<script lang="ts">
  import { onMount } from 'svelte';
  import { nebulyClient } from '../stores/nebulyClient';
  import ChartComponent from './ChartComponent.svelte';

  export let reportId: string;

  let reportData = null;
  let isLoading = true;
  let error = null;

  onMount(async () => {
    try {
      isLoading = true;
      const client = $nebulyClient;
      reportData = await client.getReport({ reportId });
    } catch (err) {
      error = err;
    } finally {
      isLoading = false;
    }
  });
</script>

{#if error}
  <div>Error: {error.message}</div>
{:else if isLoading}
  <div>Loading...</div>
{:else}
  <div style="display: flex; flex-direction: column; gap: 1rem;">
    <h1>{reportData.title}</h1>
    {#each reportData.charts as chart (chart.id)}
      <div>
        <ChartComponent {chart} />
      </div>
    {/each}
  </div>
{/if}

ChartComponent: Rendering Nebuly Chart Types

The ChartComponent helper function is responsible for rendering the appropriate Nebuly chart web component based on the chart type. You can set up your chart option objects and extend the switch statement to support all the chart types you need:
<!-- ChartComponent.svelte -->
<script lang="ts">
  import type { ChartData } from '@nebuly-ai/reports';
  import type { ChartCustomizationOptions } from '@nebuly-ai/reports/web';

  export let chart: ChartData;

  // Define your chart options as needed.
  const lineOptions: ChartCustomizationOptions = { /* ... */ };
  const metricOptions: ChartCustomizationOptions = { /* ... */ };
  const horizontalBarOptions: ChartCustomizationOptions = { /* ... */ };

  $: chartDataString = JSON.stringify(chart.data);
  $: chartOptionsString = (() => {
    switch (chart.type) {
      case 'line_chart':
        return JSON.stringify(lineOptions);
      case 'metric':
        return JSON.stringify(metricOptions);
      case 'horizontal_bar_chart':
        return JSON.stringify(horizontalBarOptions);
      // Add additional chart type cases here as needed
      default:
        throw new Error(`Unknown chart type: ${chart.type}`);
    }
  })();
</script>

{#if chart.type === 'line_chart'}
  <nebuly-line-chart data={chartDataString} options={chartOptionsString} />
{:else if chart.type === 'metric'}
  <nebuly-metric-chart data={chartDataString} options={chartOptionsString} />
{:else if chart.type === 'horizontal_bar_chart'}
  <nebuly-horizontal-bar-chart data={chartDataString} options={chartOptionsString} />
{/if}
I