Skip to main content
Web components are not supported in server-side rendering (SSR) environments. This example assumes that your Vue.js application is running entirely on the browser (client-side only). If you attempt to use the chart component library within SSR frameworks (such as Nuxt.js) 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 Vue Provide/Inject

Next, it’s best practice in Vue to provide the Nebuly client throughout your app using Vue’s provide/inject system. This lets any component access the client instance via inject, without having to pass it through props manually. Here’s how you can create a composable for the Nebuly client:
// composables/useNebulyClient.ts
import NebulyReports from '@nebuly-ai/reports';
import { provide, inject, type InjectionKey } from 'vue';

const NebulyClientKey: InjectionKey<InstanceType<typeof NebulyReports.Client>> = Symbol('nebuly-client');

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

export function useNebulyClient(): InstanceType<typeof NebulyReports.Client> {
  const client = inject(NebulyClientKey);
  if (!client) {
    throw new Error('useNebulyClient must be used within a component that provides NebulyClient');
  }
  return client;
}

Registering Nebuly Web Components in Vue

To use Nebuly web components (such as charts) in your Vue 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 Vue application setup:
// main.ts
import { createApp } from 'vue';
import NebulyReports from '@nebuly-ai/reports/web';
import { provideNebulyClient } from './composables/useNebulyClient';
import App from './App.vue';

NebulyReports.registerComponents();

const app = createApp(App);
app.provide = provideNebulyClient;
app.mount('#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, uses a data-fetching library like VueUse, and handles loading and error states gracefully. Here’s an example implementation:
<template>
  <div>
    <div v-if="error">
      Error: {{ error.message }}
    </div>
    <div v-else-if="isLoading">
      Loading...
    </div>
    <div v-else>
      <button
        v-for="report in reports"
        :key="report.id"
        @click="goToReport(report.id)"
      >
        {{ report.title }}
      </button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useNebulyClient } from '../composables/useNebulyClient';
import { useRouter } from 'vue-router';

const router = useRouter();
const client = useNebulyClient();
const reports = ref([]);
const isLoading = ref(true);
const error = ref(null);

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

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

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:
<template>
  <div v-if="error">
    Error: {{ error.message }}
  </div>
  <div v-else-if="isLoading">
    Loading...
  </div>
  <div v-else style="display: flex; flex-direction: column; gap: 1rem;">
    <h1>{{ reportData.title }}</h1>
    <div v-for="chart in reportData.charts" :key="chart.id">
      <ChartComponent :chart="chart" />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useNebulyClient } from '../composables/useNebulyClient';
import ChartComponent from './ChartComponent.vue';

const props = defineProps<{
  reportId: string;
}>();

const client = useNebulyClient();
const reportData = ref(null);
const isLoading = ref(true);
const error = ref(null);

onMounted(async () => {
  try {
    isLoading.value = true;
    reportData.value = await client.getReport({ reportId: props.reportId });
  } catch (err) {
    error.value = err;
  } finally {
    isLoading.value = false;
  }
});
</script>

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:
<template>
  <component :is="chartComponent" :data="JSON.stringify(chart.data)" :options="JSON.stringify(chartOptions)" />
</template>

<script setup lang="ts">
import type { ChartData } from '@nebuly-ai/reports';
import type { ChartCustomizationOptions } from '@nebuly-ai/reports/web';
import { computed } from 'vue';

const props = defineProps<{
  chart: ChartData;
}>();

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

const chartComponent = computed(() => {
  switch (props.chart.type) {
    case 'line_chart':
      return 'nebuly-line-chart';
    case 'metric':
      return 'nebuly-metric-chart';
    case 'horizontal_bar_chart':
      return 'nebuly-horizontal-bar-chart';
    // Add additional chart type cases here as needed
    default:
      throw new Error(`Unknown chart type: ${props.chart.type}`);
  }
});

const chartOptions = computed(() => {
  switch (props.chart.type) {
    case 'line_chart':
      return lineOptions;
    case 'metric':
      return metricOptions;
    case 'horizontal_bar_chart':
      return horizontalBarOptions;
    default:
      return {};
  }
});
</script>
I