Skip to content
Reeflow
Start Building

React

Use Reeflow components in your React applications with full TypeScript support and React-specific hooks.

Terminal window
npm install @reeflow/react
import { ReeflowProvider, ReeflowBarChart } from '@reeflow/react';
function App() {
return (
<ReeflowProvider authToken="your-api-key">
<ReeflowBarChart
title="Revenue by Product"
query={{
type: 'aggregate',
query: {
measures: [{ column: 'orders.total_amount', type: 'sum', label: 'Revenue' }],
dimensions: [{ column: 'orders.product_name', type: 'categorical' }],
},
}}
/>
</ReeflowProvider>
);
}

The React SDK provides the useReeflowComponent hook for accessing component state, data, and control methods:

import { useReeflowComponent } from '@reeflow/react';
function MyChart() {
const { isLoading, isError, error, data } = useReeflowComponent('my-chart');
return (
<div>
{isLoading && <p>Loading...</p>}
{isError && <p>Error: {error?.message}</p>}
{data && <p>Loaded {data.row_count} rows</p>}
<ReeflowBarChart id="my-chart" query={query} />
</div>
);
}

See React Hooks for complete documentation.

All React components include auto-generated event handler props for standard events (onStateChange, onDataLoaded, onError) and component-specific events:

<ReeflowBarChart
id="chart"
query={query}
onDataLoaded={(event) => console.log('Data loaded:', event.detail)}
onChartClick={(event) => console.log('Chart clicked:', event.detail)}
/>;

You can pass query objects directly inline without needing useMemo(). The SDK automatically performs deep equality checks to prevent unnecessary re-executions:

// ✅ This works perfectly - no useMemo needed!
<ReeflowBarChart
query={{
type: 'aggregate',
query: {
measures: [{ column: 'revenue', type: 'sum' }],
},
}}
/>;

You can still use useMemo() if the query depends on props or state variables:

const query = useMemo(
() => ({
type: 'aggregate',
query: {
measures: [{ column: selectedField, type: 'sum' }],
},
}),
[selectedField], // Re-create only when selectedField changes
);

TypeScript will infer better types when you use as const:

const query = {
type: 'aggregate' as const, // ✅ Infers 'aggregate', not string
query: {
measures: [
{ column: 'revenue', type: 'sum' as const }, // ✅ Infers 'sum', not string
],
},
};

For accessing component state, prefer hooks over refs:

function MyChart() {
const { data, isLoading } = useReeflowComponent('chart');
return (
<div>
{isLoading && <Spinner />}
<ReeflowBarChart id="chart" query={query} />
</div>
);
}

Always provide user feedback:

function MyChart() {
const { isLoading, isError, error } = useReeflowComponent('my-chart');
return (
<div>
{isLoading && <LoadingSpinner />}
{isError && <ErrorMessage error={error} />}
<ReeflowBarChart id="my-chart" query={query} />
</div>
);
}

The React SDK is fully typed. Import types from @reeflow/core:

import type { QueryResult, ReeflowError, JSONQLQuery } from '@reeflow/core';
import type { ReeflowBarChartProps } from '@reeflow/react';
const query: JSONQLQuery = {
type: 'aggregate',
query: {
measures: [{ column: 'revenue', type: 'sum' }],
},
};
import { ReeflowBarChart, ReeflowLineChart, useReeflowComponent } from '@reeflow/react';
function Dashboard() {
const revenue = useReeflowComponent('revenue-chart');
const orders = useReeflowComponent('orders-chart');
const isLoading = revenue.isLoading || orders.isLoading;
return (
<div>
{isLoading && <p>Loading dashboard...</p>}
<ReeflowLineChart
id="revenue-chart"
title="Revenue"
query={{
type: 'aggregate',
query: {
measures: [{ column: 'orders.total_amount', type: 'sum' }],
dimensions: [{ column: 'orders.order_date', type: 'time', granularity: 'month' }],
},
}}
/>
<ReeflowBarChart
id="orders-chart"
title="Orders"
query={{
type: 'aggregate',
query: {
measures: [{ column: 'orders.order_id', type: 'count' }],
dimensions: [{ column: 'orders.order_date', type: 'time', granularity: 'month' }],
},
}}
/>
</div>
);
}
import { ReeflowTable } from '@reeflow/react';
import { useState } from 'react';
function OrdersTable() {
const [selected, setSelected] = useState(null);
return (
<div>
<ReeflowTable
id="orders"
title="Orders"
query={{
type: 'table',
query: {
columns: [
{ name: 'orders.order_id' },
{ name: 'orders.customer_name' },
{ name: 'orders.total_amount' },
],
},
}}
onTableRowClick={(event) => setSelected(event.detail.row)}
/>
{selected && (
<div>
<h3>Selected Order</h3>
<pre>{JSON.stringify(selected, null, 2)}</pre>
</div>
)}
</div>
);
}