react-metabase-charts
v0.1.4
Published
Render Metabase dashboards and charts in React using MUI v7 and Recharts, with full layout customization.
Maintainers
Readme
react-metabase-charts
Drop your Metabase dashboards and charts straight into any React app — and keep full control over how they look and behave. No iframes, no copy-pasting chart code.
Instead of embedding Metabase inside a frame, react-metabase-charts talks directly to Metabase's REST API, which is available in the free / Community edition. You get the data, and you decide how it's presented: every chart and every dashboard layout is built from your own components, styled with MUI sx, and driven by your filters. That level of customization simply isn't possible with embeds.
What it does for you
- Full customization instead of embeds — you render the charts yourself and style every part, so dashboards blend seamlessly into your app instead of looking like a Metabase page in a box. (Embedding via iframe is a paid Metabase feature; this library uses the open Community API.)
- Total control — choose where charts go, how big they are, what the filters look like, and even render your own filter UI. You're never locked into Metabase's layout.
- Renders an entire dashboard by id, laid out in a responsive grid, with working filters.
- Renders a single chart by dashboard + card id — live from Metabase, or straight from data you already have.
- Handles filters for you: Metabase parameters are mapped to each card's query automatically, including "chained" filters (picking one filter limits the options of the next).
- Looks great everywhere — layouts and charts adapt to phones.
- Lets people export any chart as CSV or PNG from the card header.
Installation
npm install react-metabase-chartsThis package doesn't bundle React, MUI, Recharts, etc. — you install those yourself (faster installs, no version conflicts). Make sure your app already has:
react react-dom @mui/material @mui/icons-material
@emotion/react @emotion/styled recharts (^3.x) html2canvasThe 30-second setup
One small step before anything works: tell the library how to reach your Metabase by wrapping your app (or just the page that uses it) in MetabaseProvider.
import { MetabaseProvider, MetabaseDashboard } from 'react-metabase-charts';
function App() {
return (
<MetabaseProvider
config={{
baseUrl: 'https://metabase.example.com', // your Metabase instance
username: 'mb_user',
password: 'mb_pass',
}}
>
<MetabaseDashboard dashboardId={42} />
</MetabaseProvider>
);
}That's it — a full, filterable dashboard. (The credentials are only used in the visitor's browser to get a Metabase session token; they're not exposed on every request.)
Recommended: token-based auth (no credentials in the browser)
Sending a Metabase username/password from the browser ships those credentials
into your JS bundle. Prefer having your backend mint the session token and pass
a getSessionToken callback instead — the browser only ever holds a short-lived
Metabase session token:
<MetabaseProvider
config={{
baseUrl: 'https://metabase.example.com',
getSessionToken: async () => {
const res = await fetch('/api/metabase-token', {
headers: { Authorization: `Bearer ${localStorage.getItem('token')}` },
});
return (await res.json()).id; // your backend returns { id: <metabase session> }
},
}}
>
<MetabaseDashboard dashboardId={42} />
</MetabaseProvider>The token is fetched lazily and refreshed by the library when it approaches expiry, so the shared Metabase credentials stay server-side.
Tip: the
username/passwordmode below has no token callback and is meant for throwaway/local setups — the credentials then travel in your bundle.
Common ways to use it
1. A whole dashboard, ready to go
The simplest option, shown above. Add a couple of props to take control:
<MetabaseDashboard
dashboardId={42}
restrictedFilters={{ regiao: ['Sudeste'] }} // pre-applied + can't be removed by users
hiddenParams={['senha_interna']} // params you don't want shown at all
filterLayout="bar" // see "Filter UIs" below
/>2. Just one chart on a page
Say you only want a single card from inside a dashboard:
import { MetabaseChart } from 'react-metabase-charts';
<MetabaseChart dashboardId={42} cardId={7} filters={{ regiao: ['Sudeste'] }} />It fetches that card, applies your filters, and renders it.
3. Render data you already have
Already have the data? Skip the network — just hand it over. Tell it what type of chart to draw (line, bar, pie, ...):
<MetabaseChart
data={[
{ mes: 'jan', total: 120 },
{ mes: 'fev', total: 90 },
]}
metabaseDisplayType="line"
/>Filter UIs (choose your flavor)
Dashboard filters can be shown a few ways. Set filterLayout:
| filterLayout | What the user sees |
| --- | --- |
| both (default) | a chip bar of active filters plus a Filtrar button that opens a slide-out drawer |
| bar | just the horizontal chip bar of active filters |
| drawer | just the floating Filtrar button + drawer |
| custom | none of the built-in UI — you render your own |
Building your own filter UI? Pass renderCustomFilters. You get all the internal state and fetching (including chained values) and you decide how it looks:
<MetabaseDashboard
dashboardId={42}
filterLayout="custom"
renderCustomFilters={({ parameters, filters, setFilters, clearFilters, activeSlugs }) => (
<div>
{parameters.map((param) => (
<button
key={param.id}
onClick={() => setFilters({ ...filters, [param.slug]: ['X'] })}
>
Filter by {param.name}
</button>
))}
<button onClick={clearFilters}>Clear</button>
</div>
)}
/>Styling
Every piece of the UI accepts MUI sx props, so you can match your app's look:
<MetabaseDashboard
dashboardId={42}
sx={{ backgroundColor: 'transparent' }} // outer container
gridSx={{ gap: '16px' }}
cardSx={{ borderRadius: '8px' }}
filterBarSx={{}}
drawerSx={{}}
/>Full layout theming with layoutSx
On top of the convenience props above, MetabaseProvider, MetabaseDashboard and
MetabaseChart accept a layoutSx map that overrides every presentational
wrapper/container slot (dashboard container, grid, cards, filter bar, filter
drawer, FAB, chart wrappers, scalar, skeleton, …). It merges on top of the built-in
defaults, so you only set the slots you want to change:
<MetabaseDashboard
dashboardId={42}
layoutSx={{
container: { backgroundColor: 'transparent', p: 0 },
card: { borderRadius: 8, boxShadow: 1 },
cardHeader: { borderBottom: 'none' },
chartContent: { height: 280 },
scalarValue: { fontSize: '3.5rem', color: 'primary.main' },
filterChip: { bgcolor: 'grey.100' },
}}
/>layoutSx scopes as it nests: MetabaseProvider sets app-wide defaults, an inner
MetabaseDashboard/MetabaseChart narrows/overrides them for its subtree, and
every slot not provided keeps the library default. Overrides are plain values
evaluated against your ambient MUI theme — they are not a MUI theme merger and
do not touch other providers (e.g. drf-react-by-schema). The full list of slots
lives in the MetabaseLayoutSx type.
Mobile detection uses your MUI theme's md breakpoint by default. To detect with your own theme (or force a value), pass theme/isMobile to MetabaseProvider.
Hooks (when you want to build your own UI)
Prefer full control? MetabaseProvider also exports two hooks you can call anywhere under it:
import { useMetabaseDashboard } from 'react-metabase-charts';
function MyPage() {
const { schema, cardsData, filters, setFilters, error, isEmpty, hasErrors } =
useMetabaseDashboard(42, { regiao: ['Sudeste'] });
// schema.cardSchemas, cardsData[cardId].data / .loading / .error ...
}useMetabaseDashboard(dashboardId, restrictedFilters?)— the whole dashboard's schema, per-card data, filter state and errors.useMetabaseChart(dashboardId, cardId, filters)— one card's schema + data + loading/error.
No data or charts not loading? A quick checklist
- Missing provider — any
MetabaseDashboard,MetabaseChart, or the export buttons must live under aMetabaseProvider. You'll get a clear error if they don't. - Wrong id — double-check the
dashboardId/cardIdagainst Metabase. - Credentials — the session token comes from
config.username/password. Wrong credentials show up as an auth error in the console. - Server reachable — the browser must be able to reach
config.baseUrl. Watch the Network tab for/api/sessionand/api/dashboard/...calls. - Empty results — a card with no matching rows shows "Não há dados disponíveis". That's expected, not a bug.
License
AGPL-3.0-or-later.
