Components
Import Flare's components from @flare/plugin/vue. Flare's runtime loads them into every plugin frame, so they add nothing to your bundle, and they match the app in every theme. The set is small on purpose, and each component stays stable between versions.
<script lang="ts" setup>
import { BarChart, Callout, LineChart, SegmentedControl } from '@flare/plugin/vue';
import { ref } from 'vue';
const shape = ref('bars');
const points = [
{ label: 'Mon', value: 12 },
{ label: 'Tue', value: 18 },
{ label: 'Wed', value: 9 },
];
</script>
<template>
<div class="flex h-full flex-col gap-2 px-3 pb-3">
<SegmentedControl
v-model="shape"
:options="[
{ value: 'bars', label: 'Bars' },
{ value: 'line', label: 'Line' },
]"
/>
<div class="min-h-0 flex-1">
<BarChart v-if="shape === 'bars'" :data="points" title="Orders per day" />
<LineChart v-else :data="points" title="Orders per day" />
</div>
</div>
</template>Figures and charts
Stat
One large figure that fills its box, with a label underneath. It uses the same layout as the dashboard's built in Stat widget, so the two line up.
| Prop | What it is |
|---|---|
value | A number, a string, or null (shown as None, or … while loading). |
label | What it counts. Required. |
detail | A second line under the label, such as the source or the time. |
change | The change as a fraction, such as 0.12 for 12% up, shown signed next to the label. |
upIsGood | Whether a rise is good (green) or bad (red, as for errors). Defaults to true. |
compact | Shortens large numbers to 12.9K, with the exact figure on hover. Defaults to on. |
prefix | Text before the figure, such as a currency sign. |
loading | Whether the figure is still loading. |
inset | Adds the padding a widget body needs. Defaults to on. Turn it off in a dialog. |
BarChart
One column per label. Multiple series stack within each column and get a legend. The chart fills its container, so give the container a height.
| Prop | What it is |
|---|---|
data | One series: { label, value }[]. |
labels | For multiple series: the label of each column. |
series | For multiple series: { name, values }[], one value per label. |
format | A function that formats values on the axis and in the tooltip. |
title | For screen readers, which get a table of the data headed by it. |
color | Any CSS colour for the one series of data, such as rgb(var(--flare-violet)). The chart colour when there is none. |
LineChart
Takes the same props as BarChart. Each series is a line, with a legend. A missing value leaves a gap in the line.
ProgressBar
| Prop | What it is |
|---|---|
value | From 0 to 1, or null when progress is unknown. |
label | What it measures. Required. |
tone | info, positive or danger. |
Data
DataGrid
A table that looks like Flare's own. Only the rows in view are rendered.
| Prop | What it is |
|---|---|
columns | { name, label?, type?, key? }[]. label is the heading, when it isn't the name. |
rows | { id, values }[], the values by column name. |
selectable | Rows can be selected, with v-model:selected. |
editable | Cells can be edited; each edit emits edit. |
sort | { field, direction }, or null. |
focused | { row, column }, the cell to mark as the one being looked at, or null. |
cell | (row, column) returning how to draw a cell otherwise: { value } to draw another value, { display } with a CellDisplay whose icon is a component, or nothing to leave it. |
It emits sort, edit, focus, loadMore and link (a link in a cell drawn by cell was clicked), and has row-menu and empty slots.
ValueChip
One value in its type's colour, as in Flare's tables.
| Prop | What it is |
|---|---|
value | The value. |
nullText | Shown instead of a chip when there is none. |
Markdown
Renders Markdown in Flare's type: headings, paragraphs, lists, quotes, code and links. source is the text, size is sm (the app's own, the default) or md (for a page read on its own), and httpsOnly draws any link that isn't https as its text. It never renders HTML, so any HTML in the text shows as plain text. Links only work for http, https and mailto, and open in the browser after the user confirms.
Controls
| Component | Props |
|---|---|
Button | variant (primary, secondary, outline, ghost, danger, success), size (xs to xl), disabled, loading, icon, iconRight, type, fullWidth. Emits click. |
Input | v-model (text or a number), label, type, placeholder, icon, disabled, required, suggestions, size (xs, sm, md, lg), and the field props. |
Textarea | v-model, label, placeholder, disabled, rows, maxRows, and the field props. Grows as the user types. |
Select | v-model (text), label, icon, placeholder (shown when the value matches no option), disabled, size (md, sm), variant (default, or ghost for no box), and the field props. Pass the choices as <option> children, like a native select. Flare draws the list itself. |
Checkbox | v-model (a boolean), label (required, for screen readers), indeterminate, disabled, block, align (center, start). The slot is its visible label. |
SegmentedControl | v-model, options: { value, label, badge?, icon? }[], variant (pill, underline), size (sm, md). |
Field props
Input, Textarea and Select share a frame, so a form of them lines up and reads the same:
| Prop | What it is |
|---|---|
info | What the field is for, in a tooltip on an "i" beside the label, for what the label has no room for. |
optional | Says Optional beside the label, for a form where most fields are needed. |
hint | A line of help under the field. |
error | What is wrong with the value, shown under the field in place of the hint. The field is marked invalid. |
<script lang="ts" setup>
import { Input, Select } from '@flare/plugin/vue';
import { Mail } from 'lucide-vue-next';
import { computed, ref } from 'vue';
const email = ref('');
const plan = ref('monthly');
const problem = computed(() =>
email.value && !email.value.includes('@') ? 'An email address has an @ in it' : null
);
</script>
<template>
<div class="space-y-3.5 p-3">
<Input
v-model="email"
label="Customer email"
type="email"
:icon="Mail"
info="The address the customer signed up with."
:error="problem"
/>
<Select v-model="plan" label="Plan" optional hint="Leave it to search every plan.">
<option value="monthly">Monthly</option>
<option value="yearly">Yearly</option>
</Select>
</div>
</template>Messages
| Component | Props |
|---|---|
Badge | tone, dot, quiet (the tone as a dot only, beside grey words, for the everyday state), size (sm, md). |
Callout | tone (neutral, success, warning, danger, info), icon. A message inside the view, such as why something couldn't be read. |
EmptyState | title, description, icon, size (sm, lg), plain (no outline, inside something that has one). Put a next step in the slot, such as a button. |
A Badge's tone is a state, neutral, success, warning, danger or info, or a hue for telling kinds of things apart without meaning good or bad: blue, orange, aqua, magenta or violet. BadgeTone is the type of the states.
formatNumber(value, compact) is the number formatting the charts and figures use: 1,284, or 12.9K when compact.
Icons
Props named icon take a Vue component. Flare uses Lucide. Install it, and the icons you use are bundled with your plugin:
npm install lucide-vue-next<script lang="ts" setup>
import { Button } from '@flare/plugin/vue';
import { RefreshCw } from 'lucide-vue-next';
</script>
<template>
<Button variant="secondary" size="sm" :icon="RefreshCw">Refresh</Button>
</template>Styling
Style views with Tailwind and Flare's theme tokens, the same ones Flare's screens use: bg-flare-panel, bg-flare-surface, border-flare-border, text-flare-text, text-flare-muted, text-flare-subtle, text-flare-danger, text-flare-positive and so on. For telling kinds of things apart, such as one category from another, there are five hues that never read as a state: flare-blue, flare-orange, flare-aqua, flare-magenta and flare-violet, as in text-flare-violet or bg-flare-aqua/15. Each has its own shade in light and dark, and follows the mode like every token. Keep colour for what means something. flare-plugin build compiles the classes you use into plugin.css, at Flare's type sizes. text-xs is Flare's body text and text-2xs its secondary text.
The frame picks up theme changes as they happen, so views styled with tokens work in every theme. <style> blocks in components work too.
A widget's body fills the space under the title Flare draws, so add your own padding, such as px-3 pb-3. Command views and result views are as tall as their content, up to a limit.