Skip to main content
RapidDev - Software Development Agency
Lovable PromptsInternal ToolsBeginner

Build an Internal Dashboard in Lovable

A staff-only read-only KPI dashboard with stat cards, Recharts visualizations, date-range filters, and a pre-aggregated metrics_snapshots layer — backed by Supabase Cloud with staff-only RLS policies.

Time to MVP

~2 hours

Credits

~80–150 credits for full chain

Difficulty

Beginner

Cloud features

3

TL;DR

The one-paragraph version before you dive in.

Paste the starter prompt into Lovable Build mode and get a staff-only KPI dashboard with stat cards, Recharts visualizations, date-range filters, and a pre-aggregated metrics_snapshots table. Total build time is roughly two hours with five follow-up prompts. Expected credit burn is 80–150 credits on a Pro $25/mo plan.

Setup checklist

Complete these steps in Lovable before pasting the starter prompt — takes about 5 minutes.

Cloud tab settings

Database

Stores metrics_snapshots, dashboard_filters, and the dashboard_overview() RPC that returns all KPI numbers in one round-trip.

  1. 1Click the + button next to Preview in the top toolbar to open the Cloud tab
  2. 2Click Database — you'll see an empty Table Editor
  3. 3Leave it empty for now — the starter prompt will create all tables, RLS policies, the has_role() function, and the dashboard_overview() RPC for you

Auth

Email+password staff login. Role (admin or staff) is stored in app_metadata so it's baked into the JWT and cannot be tampered with from the client.

  1. 1Cloud tab → Users & Auth
  2. 2Under Sign-in methods, confirm Email is enabled (it is by default)
  3. 3Toggle 'Allow new users to sign up' to OFF — you'll invite staff members manually from the Users page
  4. 4After the build, set each staff user's app_metadata to {"role": "staff"} from Cloud tab → Users & Auth → click the user row → Edit

Edge Functions

The daily refresh-metrics-snapshot function aggregates your real data and writes one row per metric per day, keeping dashboard queries fast and cheap.

  1. 1Cloud tab → Edge Functions
  2. 2No manual setup needed before pasting the starter prompt — follow-up prompt #1 will scaffold the function and pg_cron schedule for you
  3. 3After follow-up #1, click the function name here to view logs and confirm it ran successfully

Preflight checklist

  • You're in a fresh Lovable project (Vite + React + TypeScript + Tailwind + shadcn/ui already scaffolded — that's the default)
  • You're on Pro $25/mo — the full 7-route dashboard with charts and optional Edge Function follow-ups runs ~80–150 credits total, which exceeds the Free plan's ~30 monthly cap
  • Your existing domain tables (orders, users, products) are in Lovable Cloud if you want the dashboard to report on real data — the starter prompt seeds 7 days of fake metrics_snapshots for the demo regardless

The starter prompt — paste this first

Copy this. Paste it into Lovable Build mode (the default chat at the bottom-left of the editor). Hit send.

lovable-agent-mode.txt
~30–50 credits
Build a staff-only internal KPI dashboard. Stack: React + Vite + TypeScript + Tailwind + shadcn/ui (already scaffolded), React Router, Supabase JS client against Lovable Cloud.

## Database schema (create one migration)

Create these tables in the public schema and enable RLS on all of them:

1. `profiles` — id (uuid, FK to auth.users.id, cascade delete, primary key), full_name (text), email (text not null), role (text not null default 'staff', check role in ('admin','staff','viewer')), created_at (timestamptz default now()).

2. `metrics_snapshots` — id (uuid pk default gen_random_uuid()), metric (text not null, e.g. 'mrr_cents', 'active_users', 'orders_count', 'new_signups_7d'), value_numeric (numeric), value_jsonb (jsonb — for breakdowns), captured_at (timestamptz default now()), period (text check in ('day','week','month')). Add an index on (metric, captured_at).

3. `dashboard_filters` — id (uuid pk default gen_random_uuid()), user_id (uuid references auth.users.id), name (text), filter_jsonb (jsonb). RLS: own-row read/write only.

Create a SECURITY DEFINER plpgsql function `has_role(check_role text) RETURNS boolean LANGUAGE plpgsql SECURITY DEFINER STABLE` that reads `(auth.jwt() -> 'app_metadata' ->> 'role')` and returns whether it matches check_role. NEVER use a SQL-language function — they get inlined by the optimizer and lose the security definer boundary.

Create a SECURITY DEFINER plpgsql function `dashboard_overview() RETURNS TABLE(total_users bigint, mrr_cents bigint, orders_today bigint, new_signups_7d bigint)` that queries your existing domain tables (or returns 0 if they don't exist yet). This gives the dashboard 4 KPI numbers in one round-trip.

RLS policies:
- profiles: SELECT by own row OR (has_role('admin') OR has_role('staff'))
- metrics_snapshots: SELECT by authenticated USING (has_role('staff') OR has_role('admin') OR has_role('viewer')); no INSERT/UPDATE/DELETE by authenticated users (writes only from service role or Edge Function)
- dashboard_filters: SELECT/INSERT/UPDATE/DELETE WHERE user_id = auth.uid()

Seed 7 days of fake metrics_snapshots rows (one row per metric per day for mrr_cents, active_users, orders_count, new_signups_7d) so charts render immediately.

## Layout

Create `src/layouts/DashboardLayout.tsx` — a two-column shell with a fixed left sidebar (220px) containing: logo at top, nav links (Overview, Revenue, Users, Activity, Settings), and a bottom-pinned user menu (email + Sign out). Main area scrolls with a DateRangeFilter pill (last 7d / 30d / 90d / custom) pinned at top-right. Apply a light mode default with shadcn ThemeProvider.

Create `src/components/StaffGuard.tsx` — calls supabase.auth.getUser(), reads app_metadata.role, shows a centered loading spinner while the session resolves, and redirects to /unauthorized if role is NOT in ('admin','staff','viewer'). Never read the role before the user object resolves.

## Pages and routes

Create these routes in src/App.tsx and page components:

- `/dashboard` — Overview: 4 StatCard components (Total Users, MRR, Orders Today, New Signups 7d) fetched from dashboard_overview() RPC, plus a revenue trend LineChart and a signups-by-day LineChart fetched from metrics_snapshots.
- `/dashboard/revenue` — MRR trend (line) + revenue by plan (stacked area), both from metrics_snapshots WHERE metric IN ('mrr_cents', ...) filtered by date range.
- `/dashboard/users` — Signups-by-day (bar) + active users by week (line).
- `/dashboard/activity` — Last 50 recent actions table (read from your existing domain tables, or show an empty state if not present).
- `/dashboard/settings` — DateRangeFilter defaults + saved filter view manager (reads/writes dashboard_filters).
- `/unauthorized` — one-line message with Sign out button.

## Components

Create these reusable components:
- `src/components/StatCard.tsx` — label, value, delta (with emerald-500 for positive / rose-500 for negative), sparkline (inline Recharts LineChart, no axes).
- `src/components/TimeSeriesChart.tsx` — Recharts ResponsiveContainer + LineChart/AreaChart wrapper. Accepts data, metric key, date range. Shows a shadcn Skeleton while loading. Shows an empty-state illustration when data is an empty array. Uses shadcn chart palette tokens.
- `src/components/DateRangeFilter.tsx` — Pill buttons for last 7d / 30d / 90d plus a calendar popover for custom range. On change, calls an onRangeChange callback that triggers query refetch.
- `src/components/SavedFilterChips.tsx` — reads dashboard_filters for current user, renders named chips, click to restore the saved range.

## Styling

Light mode default. Primary color: slate-900. Positive deltas: emerald-500. Negative deltas: rose-500. Four stat cards on desktop (2-up tablet, 1-up mobile). Chart lines use shadcn chart palette tokens. Table rows zebra-striped on hover.

Generate the migration first, confirm it looks correct, then generate the layout + StaffGuard, then the pages in order.

What this prompt generates

  • Creates a SQL migration with 3 tables (profiles, metrics_snapshots, dashboard_filters), has_role() SECURITY DEFINER function, dashboard_overview() RPC, all RLS policies, and 7 days of seed data
  • Scaffolds DashboardLayout.tsx with sidebar nav, user menu, and DateRangeFilter, wrapped in StaffGuard
  • Generates 5 page components (Overview, Revenue, Users, Activity, Settings) wired to React Router
  • Builds reusable StatCard, TimeSeriesChart, DateRangeFilter, and SavedFilterChips components
  • Wires all chart queries to metrics_snapshots via date-range filter — never raw domain tables

Paste into: Lovable Build mode (the default chat at the bottom-left of the editor)

Expected output

What Lovable will generate after the starter prompt runs successfully.

Files
supabase/migrations/0001_internal_dashboard_schema.sql

3 tables + RLS + has_role() + dashboard_overview() RPC + 7-day seed data

src/layouts/DashboardLayout.tsx

Sidebar + DateRangeFilter pill + user menu shell

src/components/StaffGuard.tsx

Reads JWT app_metadata.role, shows spinner while loading, redirects non-staff to /unauthorized

src/components/StatCard.tsx

KPI card with label, value, delta, and sparkline

src/components/TimeSeriesChart.tsx

Recharts wrapper with loading skeleton and empty state

src/components/DateRangeFilter.tsx

Pill buttons + custom calendar popover, fires onRangeChange

src/components/SavedFilterChips.tsx

Reads and restores per-user saved date ranges from dashboard_filters

src/hooks/useDashboardMetrics.ts

TanStack Query hook that fetches from metrics_snapshots with staleTime: 60_000

src/pages/Dashboard.tsx

Overview with 4 StatCards + 2 charts

src/pages/Revenue.tsx

MRR trend + revenue-by-plan stacked area chart

src/pages/Users.tsx

Signups-by-day + active-users-by-week charts

src/pages/Activity.tsx

Recent activity table (last 50 rows)

src/pages/Settings.tsx

Filter defaults + saved views manager

src/pages/Unauthorized.tsx

Fallback for non-staff users

Routes
/dashboard

Overview — 4 KPI cards + revenue trend + signups chart

/dashboard/revenue

MRR trend + revenue by plan breakdown

/dashboard/users

Signups by day + active users by week

/dashboard/activity

Recent system activity table

/dashboard/settings

Date-range defaults + saved views

/unauthorized

Shown when a non-staff user lands on /dashboard/*

DB Tables
profiles
ColumnType
iduuid primary key references auth.users(id)
full_nametext
emailtext not null
roletext not null default 'staff'
created_attimestamptz default now()

RLS: Own-row SELECT for everyone; staff and admin can SELECT all rows (read-only)

metrics_snapshots
ColumnType
iduuid primary key default gen_random_uuid()
metrictext not null
value_numericnumeric
value_jsonbjsonb
captured_attimestamptz default now()
periodtext

RLS: SELECT by staff/admin/viewer via has_role(); no INSERT/UPDATE/DELETE for authenticated users — writes only via service role or Edge Function

dashboard_filters
ColumnType
iduuid primary key default gen_random_uuid()
user_iduuid references auth.users(id)
nametext
filter_jsonbjsonb

RLS: Own-row read/write only — each staff member saves their own date-range views

Components
StaffGuard

Session-aware role check — shows spinner while loading, redirects non-staff

StatCard

KPI card with value, delta indicator, and inline sparkline

TimeSeriesChart

Recharts wrapper with loading skeleton, empty state, and shadcn chart palette

DateRangeFilter

Date preset pills + custom calendar popover

SavedFilterChips

Per-user named filter views stored in dashboard_filters

Follow-up prompts

Paste these into Agent Mode one by one, in order, after the starter prompt finishes.

1

Wire a daily metrics refresh cron

Daily pg_cron Edge Function that aggregates real domain tables into metrics_snapshots at 03:00 UTC

~40 credits
prompt
Create a Supabase Edge Function at `supabase/functions/refresh-metrics-snapshot/index.ts` that:
1. Queries the real domain tables (orders, users, products if they exist) to compute: total_users, mrr_cents (sum of paid orders today if available), orders_count (today's order count), new_signups_7d (users created in last 7 days).
2. For each metric, inserts one row into metrics_snapshots with period='day' and captured_at=now().
3. Returns a JSON summary of what was written.

Then add a pg_cron job to schedule it: in the migration or a new SQL file, run `SELECT cron.schedule('refresh-metrics-daily', '0 3 * * *', $$SELECT net.http_post(url:='<YOUR_EDGE_FUNCTION_URL>/refresh-metrics-snapshot', headers:='{"Authorization": "Bearer " || current_setting(''app.service_role_key'')}')$$);`. Document that the URL must be replaced with the actual Edge Function URL from Cloud tab → Edge Functions after deploy.

When to use: After 5+ days of real data exists in your domain tables

2

Connect a warehouse panel (Snowflake or BigQuery)

Warehouse-connector panel on Revenue page via Edge Function proxy with named queries only

~60 credits
prompt
Add a warehouse query panel to /dashboard/revenue. Steps:
1. In Cloud tab → Integrations, enable the Snowflake (or BigQuery) App connector for this project.
2. Create an Edge Function at `supabase/functions/warehouse-query/index.ts` that accepts a POST body with `{query_name: string, params: object}`. The function runs a SAVED named query (hardcoded switch statement — never accept free-form SQL from the client) against the connector and returns shaped JSON. Add WAREHOUSE_NAMED_QUERY_TIMEOUT=30000 to Cloud Secrets.
3. Add a new WarehouseChart component in src/components/WarehouseChart.tsx that calls the warehouse-query Edge Function with the current date range and renders the result in a Recharts LineChart.
4. Render WarehouseChart below the existing Recharts charts on /dashboard/revenue with a 'Warehouse data' label and a 'Last refreshed' timestamp.

When to use: When your real data lives in Snowflake, BigQuery, or Databricks and is too large for Supabase

3

Add CSV export per chart

Date-stamped CSV export button on every chart card, routed through an Edge Function

~30 credits
prompt
Create an Edge Function at `supabase/functions/dashboard-export-csv/index.ts` that:
1. Accepts POST with `{metric: string, from_date: string, to_date: string}` in the body.
2. Queries metrics_snapshots WHERE metric = $1 AND captured_at BETWEEN $2 AND $3.
3. Returns a text/csv response with headers `metric,value_numeric,captured_at,period` and Content-Disposition: attachment; filename="{metric}_{from}_{to}.csv".

In each TimeSeriesChart card, add an 'Export CSV' button (shadcn Button, size sm, variant outline) at the card's top-right. On click, POST to the edge function with current metric name and date range, then trigger a browser download via `URL.createObjectURL(blob)`.

When to use: When stakeholders ask to email the numbers or drop them into a spreadsheet

4

Add saved date-range filters per user

Per-user named date-range views that persist across sessions and appear in both the filter pill and settings page

~25 credits
prompt
Extend the DateRangeFilter and SavedFilterChips components:
1. Add a 'Save current view' button inside DateRangeFilter. On click, open a shadcn Dialog with a text input for the view name. On confirm, insert a row into dashboard_filters with `{name, filter_jsonb: {range_type, custom_start, custom_end}}`.
2. In SavedFilterChips, render each saved view as a shadcn Badge with an X button. Clicking the badge restores the range. Clicking X deletes the row.
3. On /dashboard/settings, render a full list of all saved views (name + range summary + delete button) from the current user's dashboard_filters rows.

When to use: Once people use the dashboard daily and want to switch between 'This week' and 'Last quarter' in one click

5

Add an AI weekly summary widget

AI-generated 3-bullet weekly summary card at the top of the Overview page

~50 credits
prompt
Create an Edge Function at `supabase/functions/weekly-summary/index.ts` that:
1. Queries the last 7 days of metrics_snapshots rows grouped by metric.
2. Formats them into a plain-text summary: 'Metrics for the week of {date}: mrr_cents={value}, active_users={value}, orders_count={value}, new_signups_7d={value}. Previous week: ...'.
3. Calls Lovable AI (Gemini 3 Flash via the built-in AI panel) or directly calls the Anthropic API with key from Cloud Secrets (ANTHROPIC_API_KEY) using Claude Haiku 4.5 to generate a 3-bullet plain-English summary.
4. Returns the 3-bullet string.

In src/pages/Dashboard.tsx, add a shadcn Card at the very top of the page with title 'This week at a glance'. On mount, POST to weekly-summary and render the result. Show a shadcn Skeleton while loading.

When to use: When you want exec-level plain-English insight without reading 4 separate charts

6

Add drill-down from KPI card to raw rows

Clickable KPI cards that open a drill-down Sheet showing the underlying 100 rows

~40 credits
prompt
Make each StatCard clickable. On click, open a shadcn Sheet (right drawer, 560px wide) with:
1. A title like 'Last 100 orders contributing to this KPI'.
2. A table showing the underlying domain rows (e.g., orders for mrr_cents, users for new_signups_7d).
3. The same has_role('staff') RLS check that governs metrics_snapshots governs the underlying tables — the Sheet query fails cleanly with an error message if the domain table doesn't exist or lacks a staff-read policy.

Create a DrillDownSheet component in src/components/DrillDownSheet.tsx that accepts `{metric, dateRange, isOpen, onClose}` and maps each metric name to its source table and columns.

When to use: Once stakeholders start asking 'which orders made up that MRR number?'

Common errors

Real error strings you'll see. Find yours, paste the fix prompt.

Charts render fine in preview but show 0 everywhere on the published site

Lovable's preview uses the service-role Supabase key, which bypasses RLS. Your production site uses the anon/authenticated key, which respects RLS. Your metrics_snapshots table has RLS enabled but no SELECT policy for authenticated users.

Fix — paste into Lovable Agent Mode
Add an RLS policy on metrics_snapshots: `CREATE POLICY staff_read_metrics ON metrics_snapshots FOR SELECT TO authenticated USING (has_role('staff') OR has_role('admin') OR has_role('viewer'));`. Confirm has_role() is the SECURITY DEFINER plpgsql variant reading from JWT app_metadata, NOT a SQL function reading from the profiles table. Re-test in the published site (not the preview).
supabaseUrl is required — error on the published site, not in preview

VITE_SUPABASE_URL and VITE_SUPABASE_PUBLISHABLE_KEY were not set in your Vercel or Netlify environment variables before the build. These values are baked in at build time, not injected at runtime.

Manual fix

Go to Vercel Dashboard → Settings → Environment Variables. Add VITE_SUPABASE_URL, VITE_SUPABASE_PUBLISHABLE_KEY, and VITE_SUPABASE_PROJECT_ID from Cloud tab → Integrations → Supabase → connection details. Trigger a new deployment (the env vars only apply to new builds, not the already-deployed one).

Dashboard page takes 8+ seconds to load and Cloud credit usage spikes

A chart component is querying a raw domain table (e.g., SELECT * FROM orders) and aggregating in JavaScript. Recharts re-runs that query on every filter change, scanning the full table each time.

Fix — paste into Lovable Agent Mode
Replace the raw query in TimeSeriesChart with a fetch from `metrics_snapshots` filtered by metric and captured_at range. If metrics_snapshots is empty, create the refresh-metrics-snapshot Edge Function (follow-up #1) to populate it. Add an index on `metrics_snapshots(metric, captured_at)`. Wrap the fetch in a TanStack Query call with `staleTime: 60_000` so filter changes within the same minute don't trigger a refetch.
permission denied for table orders when a staff user opens /dashboard/revenue

Your orders table has RLS enabled with policies for the customer or admin role only. Staff users have no SELECT policy on the domain table the Revenue chart is trying to query.

Fix — paste into Lovable Agent Mode
Run in SQL Editor: `CREATE POLICY staff_read_orders ON orders FOR SELECT TO authenticated USING (has_role('staff') OR has_role('admin'));`. Apply the same pattern to every domain table the dashboard touches. Long-term solution: only read from metrics_snapshots (which already has the correct policy) rather than raw domain tables.
Snowflake connector returns 'credentials not found' in production

Lovable's App connectors work in the deployed app but must be enabled at the Workspace level, not just configured in a chat session. The connector credential is also bound to the domain — rotating it or deploying to a new domain breaks the OAuth refresh token.

Manual fix

Cloud tab → Integrations → Snowflake → confirm the 'Enabled for this project' toggle is ON. Re-enter or rotate the connector credential. Redeploy the app so the new OAuth refresh token is bound to the production domain. Repeat after any custom domain change.

Cost reality

What this build actually costs — no surprises on your card.

Recommended Lovable plan

Pro $25/mo recommended — Free plan's ~30 monthly credits cover a single-page demo but not the full 6-route dashboard with charts, cron, and export follow-ups

Monthly run cost breakdown

~80–150 credits for starter + 3 follow-ups; add ~175 more if you do all 7 follow-ups total
ItemCost
Lovable Pro

Can drop back to Free once the dashboard is launched — the app continues to run on Cloud

$25/mo while iterating
Supabase / Lovable Cloud

Internal-dashboard traffic rarely exceeds 1K page views/mo; 500MB DB is more than enough for metrics_snapshots rows

$0/mo at MVP scale
Warehouse compute (optional)

Snowflake/BigQuery billed by query — always use saved/parameterized named queries via Edge Function, never free-form SQL

$0–50/mo
Custom domain

Point a subdomain (e.g., dashboard.yourcompany.com) at the published Lovable app

~$10–15/yr

Scaling notes: An internal dashboard rarely scales to the point where Supabase Free tier becomes the bottleneck — 50 staff viewers will never hit the 50K MAU or 500MB DB cap. The only realistic cost spike is wiring a warehouse connector that runs free-form SQL — one bad query can cost $5–50 in Snowflake/BigQuery compute. Always use named/parameterized queries inside the warehouse-query Edge Function and set a hard 30-second query timeout.

Production checklist

Steps to take before you share the URL with real users.

Domain & SSL

  • Point a custom subdomain at your published Lovable app

    Publish the app via the Publish icon top-right → Settings → Custom domain. Add a CNAME record at your DNS provider pointing dashboard.yourcompany.com to your lovable.app URL. SSL is auto-provisioned.

Auth hardening

  • Set staff role in app_metadata for every team member

    Cloud tab → Users & Auth → click each user row → Edit → set app_metadata to {"role": "staff"} (or "admin"). Role must be in app_metadata, NOT user_metadata — user_metadata is client-writable and can be spoofed.

  • Disable new user sign-up

    Cloud tab → Users & Auth → Sign-in methods → toggle 'Allow new users to sign up' to OFF so only manually-invited users can access the dashboard.

Monitoring

  • Verify the daily cron ran successfully

    Cloud tab → Edge Functions → refresh-metrics-snapshot → Logs. Confirm one run per day at ~03:00 UTC with a 200 status. Set up a Resend alert email from inside the Edge Function if the metrics computation fails.

Backups

  • Confirm daily backups are enabled

    Cloud tab → Database → Backups. Lovable Cloud takes daily backups retained up to ~14 days. Verify the most recent backup timestamp shows today's date.

Frequently asked questions

Does this work on the Free Lovable plan for a tiny team?

The starter prompt alone runs about 30–50 credits, which is close to the Free plan's ~30 monthly cap. You can build the basic dashboard on Free but you'll likely exhaust credits before finishing the date-range filter and all six routes. For a production-ready internal dashboard with the cron follow-up, Pro $25/mo is the realistic minimum.

Why do I need metrics_snapshots if my data is already in Supabase?

Without a pre-aggregation layer, every chart load triggers a full table scan (e.g., SELECT SUM(total_cents) FROM orders). That's fine with 100 rows in preview, but with 50K real orders it adds 3–8 seconds of page load time and can burn Lovable Cloud bandwidth credits at scale. The metrics_snapshots layer stores one row per metric per day — queries hit an indexed, tiny table instead of your full domain tables. The daily refresh cron (follow-up #1) keeps those numbers current.

Can I connect Snowflake or BigQuery without writing SQL myself?

Yes — Lovable's App connectors for Snowflake and BigQuery appear under Cloud tab → Integrations and can be enabled with a few clicks. You still need to write the named queries that the warehouse-query Edge Function will run, but the starter prompt and follow-up #2 give you the Edge Function template. The critical rule: never pass free-form SQL from the frontend — only call named/parameterized queries from the Edge Function to keep warehouse compute costs predictable.

How do I prevent Recharts from re-rendering on every keystroke in the date filter?

Wrap your chart data fetches in TanStack Query with staleTime: 60_000 (one minute). This means rapid filter changes within the same minute reuse the cached result instead of firing a new query. For the custom date range input, add a 300ms debounce before the filter value updates the query key. Both patterns are noted in the starter prompt for TimeSeriesChart.

What's the cheapest way to share a dashboard with a non-staff stakeholder?

The lowest-friction option is to create them a Lovable Cloud user with role='viewer', which gives them read-only access through the normal sign-in flow. If they should not have a login, follow-up #7 (embed mode) generates a signed-JWT embed URL you can paste into Notion or a client portal — the URL is time-limited and renders the chart without exposing the full dashboard.

Can I embed this dashboard in Notion or another tool we already use?

Yes — follow-up #7 adds a /embed/:dashboardId?token= route that validates a short-lived JWT, renders a chart without sidebar or auth UI, and is iframeable. Paste the embed URL into a Notion page using the /embed block. The signed token means the URL works for anyone with the link for its validity period (e.g., 24 hours), then expires. For permanent embeds, generate a new token daily via a scheduled Edge Function.

When should I move off Lovable and use Metabase or Retool instead?

Move to Metabase if your team wants ad-hoc SQL exploration alongside dashboards — Metabase's query builder is purpose-built for that and Lovable is not. Move to Retool if you need write-heavy admin forms alongside dashboards in one product (Retool combines both). Stay in Lovable if your data is already in Supabase or Lovable Cloud, you have fewer than 10 staff viewers, and you want a custom-branded KPI surface nobody outside your team will ever see — the run cost is near $0 forever and you control every pixel. If your build outgrows this prompt kit and you need custom architecture, RapidDev builds production-grade Lovable apps at $13K-$25K — book a free 30-minute consultation at rapidevelopers.com.

Need a production-grade version?

RapidDev builds production-grade Lovable apps at $13K–$25K. Book a free 30-minute consultation.

Book a free consultation

30-min call. No commitment.

Want this built for you?

We ship production apps at a fixed price — $13K–$25K, 6–10 weeks, source code yours. You've seen what it takes; we do it every week.

Get a fixed-price quote

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.