Chart Tooltip

Apply Typlog presentation to Unovis tooltip interactions.
alpha

ChartTooltip is a themed adapter for Unovis' VisTooltip. Pair it with ChartCrosshair inside an XY container for the default interactive crosshair experience. ChartTooltipContent provides the consistent label, indicator, series, and tabular-value layout used by Typlog dashboards. Pass supported Unovis primitives to targets to generate triggers automatically:

vue
<ChartTooltip :targets="[Donut]" :content-props="{ hideLabel: true }" />

Automatic targets currently support Donut (including pie charts), GroupedBar, and StackedBar. Use contentComponent and contentProps to customize the renderer. Explicit triggers are merged with generated triggers and take precedence when they use the same selector. For a custom mapping, pass a descriptor with primitive and payload:

vue
<ChartTooltip
  :targets="[{
    primitive: Donut,
    payload: arc => ({ browser: arc.data.browser }),
  }]"
/>

ChartCrosshair remains the recommended interaction for line and area charts.

vue
<script setup lang="ts">
import { VisAxis, VisGroupedBar, VisXYContainer } from '@unovis/vue'
import {
  ChartCrosshair,
  ChartRoot,
  ChartTooltip,
  type ChartConfig,
} from '@typlog/ui/charts'

interface Point {
  day: string
  desktop: number
  mobile: number
}

const data: Point[] = [
  { day: 'Mon', desktop: 186, mobile: 80 },
  { day: 'Tue', desktop: 305, mobile: 200 },
  { day: 'Wed', desktop: 237, mobile: 120 },
  { day: 'Thu', desktop: 173, mobile: 190 },
  { day: 'Fri', desktop: 209, mobile: 130 },
]
const config = {
  day: { label: 'Day', role: 'x' },
  desktop: { label: 'Desktop' },
  mobile: { label: 'Mobile' },
} satisfies ChartConfig
const x = (_item: Point, index: number) => index
const y = [(item: Point) => item.desktop, (item: Point) => item.mobile]
const xTickFormat = (value: number) => data[value]?.day ?? ''
</script>

<template>
  <ChartRoot v-slot="{ mounted }" :config="config">
    <VisXYContainer v-if="mounted" :data="data" :height="240">
      <VisGroupedBar :x="x" :y="y" />
      <VisAxis
        type="x"
        :tick-format="xTickFormat"
        :tick-values="data.map((_item, index) => index)"
        :tick-line="false"
        :domain-line="false"
        :grid-line="false"
      />
      <VisAxis
        type="y"
        :tick-format="() => ''"
        :tick-line="false"
        :domain-line="false"
        :grid-line="true"
      />
      <ChartTooltip />
      <ChartCrosshair />
    </VisXYContainer>
  </ChartRoot>
</template>

Indicators

Use dot, line, dashed, or none. Icons declared in ChartConfig replace the indicator for their series.

vue
<script setup lang="ts">
import { VisLine, VisXYContainer } from '@unovis/vue'
import {
  ChartCrosshair,
  ChartRoot,
  ChartTooltip,
} from '@typlog/ui/charts'

interface Point {
  value: number
}

const data: Point[] = [{ value: 28 }, { value: 42 }, { value: 35 }, { value: 58 }, { value: 64 }]
const config = { value: { label: 'Visitors' } }
const x = (_item: Point, index: number) => index
const y = (item: Point) => item.value
const lineContentProps = {
  indicator: 'line' as const,
  labelFormatter: (value: string | number | Date) => `Day ${Number(value) + 1}`,
}
const noIndicatorContentProps = {
  indicator: 'none' as const,
  labelFormatter: (value: string | number | Date) => `Day ${Number(value) + 1}`,
}
</script>

<template>
  <div class="grid gap-6 sm:grid-cols-2">
    <ChartRoot v-slot="{ mounted }" :config="config">
      <VisXYContainer v-if="mounted" :data="data" :height="180">
        <VisLine :x="x" :y="y" />
        <ChartTooltip />
        <ChartCrosshair :content-props="lineContentProps" />
      </VisXYContainer>
    </ChartRoot>
    <ChartRoot v-slot="{ mounted }" :config="config">
      <VisXYContainer v-if="mounted" :data="data" :height="180">
        <VisLine :x="x" :y="y" />
        <ChartTooltip />
        <ChartCrosshair :content-props="noIndicatorContentProps" />
      </VisXYContainer>
    </ChartRoot>
  </div>
</template>

Formatters and icons

labelFormatter formats the final heading value. The heading priority is hideLabel, explicit labelKey, the payload field configured with role: 'x', then the Unovis crosshair x value. valueFormatter receives each series value and config key.

vue
<script setup lang="ts">
import { VisLine, VisXYContainer } from '@unovis/vue'
import {
  ChartCrosshair,
  ChartRoot,
  ChartTooltip,
} from '@typlog/ui/charts'

interface Point {
  revenue: number
}

const data: Point[] = [{ revenue: 2800 }, { revenue: 4200 }, { revenue: 3500 }, { revenue: 5800 }]
const config = { revenue: { label: 'Revenue', icon: 'ri:money-dollar-circle-line' } }
const contentProps = {
  labelFormatter: (value: string | number | Date) => `Week ${Number(value) + 1}`,
  valueFormatter: (value: unknown) => `$${Number(value).toLocaleString()}`,
}
</script>

<template>
  <ChartRoot v-slot="{ mounted }" :config="config">
    <VisXYContainer v-if="mounted" :data="data" :height="200">
      <VisLine :x="(_item: Point, index: number) => index" :y="(item: Point) => item.revenue" />
      <ChartTooltip />
      <ChartCrosshair :content-props="contentProps" />
    </VisXYContainer>
  </ChartRoot>
</template>

The rendered tooltip string is generated by Vue, so text values are escaped before Unovis inserts the HTML. A custom slot can still return arbitrary markup and should only render trusted application content.

Crosshair API

ChartCrosshair forwards Unovis crosshair props while reserving template for the configured Vue content component. Use contentComponent to provide a custom renderer and contentProps for its static props.

PropDefaultType
contentComponentChartTooltipContent
Component

Vue component rendered as the crosshair tooltip content.

contentProps{}
Record<string, unknown>

Props passed to contentComponent in addition to payload, config, and x.

Last updated Sep 6, 2026