BoilerPrompt

Lovable prompts

Full-stack web apps from a chat prompt, with Supabase wired in.

20 prompts, every one free to copy. Jump to: App, Backend, Features, Frontend, Refactor, Testing

What Lovable is good at

Lovable is a browser-based builder that turns a written spec into a working full-stack web application. You describe the product in chat, and it generates a React frontend styled with Tailwind; when the app needs persistence, auth, or server logic, it reaches for Supabase: Postgres tables, Row Level Security policies, edge functions, and storage. Every message becomes an edit to a live preview you can click through, and the project can sync to GitHub, so you can eject to your own editor when the chat loop stops being the fastest path.

Among its neighbors, v0 concentrates on React UI components rather than whole products, Bolt gives you an in-browser IDE where you edit the generated files directly, and Replit Agent builds and hosts on Replit's infrastructure. Lovable is strongest when you want a complete product, database included, without touching a terminal. It fits founders validating an idea and developers who want a credible starting point to extend.

How to prompt Lovable

Lovable rewards specs and punishes one-liners. A strong first prompt reads like a short PRD with labeled sections: what the product is, the pages, the data model, key behaviors, and the states you care about. Two hundred to three hundred words is a reasonable target; below that, the tool fills gaps with defaults you will spend messages undoing.

Always specify:

- Tables and access. Name the tables, list important columns, and say who can read and write which rows. Lovable creates Supabase migrations and Row Level Security policies from this text, and vague data descriptions produce vague schemas. - Auth boundaries. Say whether login exists and which routes sit behind it. - Empty, loading, and error states. Unprompted, you get the happy path only. - What not to touch when editing an existing app.

Iterate one feature per message and click through the preview before sending the next; stacking three requests in one message makes failures hard to attribute. When something breaks, paste the exact error text and name the screen where it happened. Put standing decisions like stack choices and styling rules into the project's Knowledge so you stop repeating them, and connect GitHub early so every change lands in version control you can diff or revert outside the chat.

All 20 Lovable prompts

App 5 prompts

Build a multi-tenant SaaS starter where teams collaborate on {{resource}}.

Auth and accounts:
- Email and password signup and login with Supabase Auth, plus password reset. After first login, an onboarding step creates a workspace and makes that user its owner.

Workspaces:
- Tables: workspaces, workspace_members (role is owner, admin, or member), invitations.
- Owners and admins invite by email; the invite link joins the workspace after signup or login. Inviting an existing member returns a clear message instead of a duplicate row.
- A workspace switcher in the top bar for users who belong to several.
- Row Level Security policies so members only read rows in their own workspaces. Write actual policies; client-side filtering does not count.

App shell:
- Public marketing page with Free and Pro tiers as static content.
- Authenticated area: a dashboard placeholder, workspace settings (rename, member list with role management, remove member, delete workspace behind a type-to-confirm dialog), and account settings for email and password.
- Billing page showing the current plan with a disabled Upgrade button; structure it so a payment provider can slot in later, without adding keys or fake checkout now.
- Leave one clearly marked extension point where {{addon}} can be wired in after launch.

Edge cases: the last owner cannot leave or be demoted, and deleting a workspace cascades its memberships and invitations.

States: loading skeletons in the shell, a friendly empty dashboard for a new workspace, error toasts on failed mutations.

To verify, I will sign up two users, invite one into the other's workspace, confirm a plain member cannot open workspace settings, and confirm nothing from workspace A renders while workspace B is active.
Build a to-do app with Supabase auth so each user only sees their own tasks.

Screens: a login and signup screen, a main list view, and a settings page for display preferences.

Data model: a tasks table with title, notes, due_date, priority, completed_at, sort_order, and user_id, with row level security so users only read and write their own rows. Add a lists table so tasks can be grouped, with a default Inbox list created on signup.

Behaviors: inline task creation from a single input at the top of the list, optimistic checkbox toggling that writes completed_at, drag to reorder within a list persisted to sort_order, and a filter bar for Today, Upcoming, and Completed. Overdue tasks show the due date in red.

Empty and error states: a first-run state offering three sample tasks, a friendly message when a filter returns nothing, and a retry banner if a write fails so the checkbox rolls back visibly.

Styling: clean single-column layout with a max width around 640px, shadcn components, a subtle strike-through animation on completion, and a dark mode toggle stored per user.

Acceptance: signing up creates the Inbox list automatically, a task checked off leaves the Today filter after refresh, reordering survives a reload, and a second account cannot see the first account's tasks from the UI or the browser console.
Build a blog with an admin CMS in one app.

Public side: a home page listing published posts newest first with cover image, title, excerpt, and tag chips, a post page at /blog/[slug] rendering markdown with code highlighting, and a tag page filtering the list. Each post page sets its document title and description from the post record.

Admin side: routes under /admin gated by Supabase auth plus an is_editor flag on a profiles table, a posts list showing draft or published status, and an editor screen with title, a slug auto-generated from the title but editable, a markdown body with live preview, a tag picker, and a cover image uploaded to a covers storage bucket.

Data model: posts with a unique slug, status, published_at, author_id, and a tags array, with row level security allowing public reads of published rows only and writes for editors only.

Behaviors: save keeps drafts private, publish stamps published_at, unpublish returns a post to draft without deleting anything, and slug collisions get a numeric suffix.

States: an empty state on the public home when nothing is published yet, a 404 page for unknown slugs, and an editor warning before navigating away with unsaved changes.

Styling: a readable measure around 68 characters for post bodies, serif headings, and quiet admin chrome so the editor feels like a document, not a dashboard.

Acceptance: a draft is invisible when signed out, publishing makes it appear without any redeploy, an unknown slug 404s cleanly, and a non-editor account cannot open /admin or write to posts.
Build a small e-commerce store with a public catalog, a cart, and Stripe checkout.

Catalog: a products table with name, slug, price_cents, inventory_count, and images in a product-images storage bucket, a grid at /shop with category filter chips, and a product page with an image gallery and a quantity picker capped at available inventory.

Cart: stored in localStorage for guests and merged into a carts table on login, a slide-over cart panel with quantity steppers per line, and a subtotal that updates on every change. Out-of-stock items in a saved cart show a notice instead of failing silently at checkout.

Checkout: an edge function named create-checkout builds a Stripe Checkout session from server-side prices, never trusting amounts sent by the client, and a webhook function records the order and decrements inventory_count only after payment succeeds. Orders land in an orders table with line items and a status field.

Account: an order history page listing past orders with status and totals.

States: an empty cart panel with a link back to the shop, a payment-cancelled return page that keeps the cart intact, and a sold-out badge on depleted products.

Styling: product imagery dominant, a whitespace-heavy grid, and price typography consistent across card, product page, and cart.

Acceptance: adding beyond inventory is blocked at the picker, a completed test payment produces one order row and reduces inventory exactly once even if the webhook retries, guest carts survive a refresh, and a cancelled checkout creates no order.
Build a booking app where providers publish availability and clients reserve slots.

Roles: a role field on profiles distinguishing provider from client, with separate home screens after login.

Provider side: a weekly availability editor with day rows and add-window buttons creating availability_windows rows holding weekday, start_time, end_time, and slot length, plus a blocked_dates table for one-off days off. A bookings list shows upcoming appointments with a cancel action.

Client side: a provider page with a week view generated from the windows minus existing bookings and blocked dates, rendering every open slot as a button in the client's local timezone with the provider's zone noted. Choosing a slot opens a confirmation showing date, time, and duration before the insert.

Double-booking: the bookings table carries a unique constraint on provider_id plus starts_at, the insert catches the conflict error, and the client sees a slot-just-taken message with the calendar refreshed.

Cancellation: clients may cancel until a cutoff-hours value the provider sets, after which the button is disabled with the reason shown. Cancelled slots reopen immediately.

Timezones: store starts_at as timestamptz, never as separate date and time strings, and render in the viewer's zone everywhere.

States: providers with no windows see setup guidance, clients see a no-availability message with next-week navigation, and past weeks are read-only.

Acceptance: two browsers booking the same slot at once produce one booking and one polite conflict, a client in one timezone viewing a provider in another sees converted times, and cancelling reopens the slot on both calendars.

Backend 5 prompts

Build a REST API for managing {{resource}}, implemented as Supabase Edge Functions with a minimal admin page for exercising it.

Endpoints:
- POST /{{resource}}: create a record, validate required fields, return 201 with the created row.
- GET /{{resource}}: list with pagination (limit and offset params, default 20, max 100) and a sort param.
- GET /{{resource}}/:id: return the record or a 404 JSON body.
- PATCH /{{resource}}/:id: partial update, reject unknown fields with 400.
- DELETE /{{resource}}/:id: soft delete via a deleted_at column; exclude soft-deleted rows from every read.

Data model: a {{resource}} table in Postgres with id (uuid), created_at, updated_at, deleted_at, plus whatever fields fit {{resource}}. Propose the columns and wait for my confirmation before running the migration.

Behavior:
- Require a Supabase auth JWT on every endpoint; respond 401 without one.
- Scope rows to the authenticated user with Row Level Security policies, not only WHERE clauses.
- Errors are JSON: { "error": { "code", "message" } } with correct status codes (400 validation, 401 auth, 404 missing, 500 unexpected).
- Handle malformed JSON bodies and invalid uuids without the function crashing.

These endpoints must be callable with curl from outside the app; do not implement them as supabase-js calls inside React components.

Admin page: list records, a create form, and a panel showing the raw status code and JSON of the last request.

When done, print each endpoint with an example curl command against the deployed function URLs so I can verify all five paths.
Stand up a GraphQL API in this project as a single Supabase edge function named graphql, with GraphQL Yoga bundled into the function.

Schema: a {{resource}} type with id, name, status, and created_at, a paginated list query accepting limit, cursor, and a status filter, a single-record query by id, and mutations for create, update, and delete.

Resolvers: read and write through a Supabase client created from the caller's Authorization header so row level security still decides what each user can touch. Never use the service role key inside resolvers.

Errors: throw typed GraphQL errors with codes UNAUTHENTICATED, NOT_FOUND, and BAD_INPUT instead of generic 500s, and surface field-level validation messages for missing required inputs.

Frontend: add a small /playground page with a query editor textarea, a run button, and a formatted JSON result pane so I can exercise the endpoint without leaving the preview.

Hardening: handle CORS for the app origin only, and reject any query nested deeper than four levels to stop abusive queries.

Acceptance: the list query returns a cursor that fetches the next page, a mutation without a signed-in session comes back UNAUTHENTICATED, deleting a record owned by another user returns NOT_FOUND rather than confirming it exists, and the playground shows errors inline instead of crashing.
Add JWT-based authentication to this app using Supabase Auth, covering the client, the database, and the edge functions.

Flows: email and password signup with a confirmation step, sign in, a magic link option, password reset, and sign out. On signup, insert a row into a profiles table keyed to the new user id.

Route protection: redirect signed-out visitors from any authenticated route to /login with a return-to parameter, and send signed-in users away from /login. Keep the session in the Supabase client so a refresh does not log anyone out, and refresh tokens silently instead of bouncing to login when the access token expires.

Database: enable row level security on every user-owned table and write policies against auth.uid(), leaving no table with policies disabled.

Edge functions: each function reads the Authorization header, verifies the JWT through the Supabase client, and returns 401 with a JSON error body when it is missing or invalid. No function may fall back to the service role for user-scoped reads.

UI states: a loading gate while the session restores so protected pages never flash before redirect, inline errors for wrong credentials, and a resend link on the confirmation notice.

Acceptance: an expired session on a protected page recovers without a visible logout, calling an edge function with no header returns 401 not 500, password reset works end to end from the email link, and querying another user's rows from the browser console returns nothing.
Design and apply the Supabase Postgres schema for a {{resource}} app before building any screens, and show me the SQL as migration files rather than ad hoc table edits.

Tables: profiles keyed to auth.users, a {{resource}} table with owner_id, name, status, and timestamps, a comments table referencing it with cascade delete, and a join table for tags with a composite primary key.

Types and constraints: a Postgres enum for status instead of free text, not-null on every foreign key, a check constraint keeping name non-empty, and unique constraints wherever duplicates would corrupt meaning, one tag name per user for example.

Defaults and triggers: created_at defaulting to now, an updated_at column maintained by a trigger, and a trigger creating the profiles row when a user signs up.

Indexes: btree on every foreign key column, plus a composite index matching the main list query, owner_id with status and created_at descending.

Row level security: enabled on every table with select, insert, update, and delete policies written against auth.uid(), and nothing that depends on the service role.

Seed: a script inserting two demo users' worth of data so future screens have something to render.

Acceptance: the migrations run from scratch without errors, show me a query listing all tables with RLS enabled, demonstrate that user one cannot select user two's rows, and confirm the updated_at trigger fires on an update. List every migration file you created.
Build a file upload flow backed by Supabase Storage with server-side validation.

Bucket and paths: a private bucket named user-files, objects written under a path prefixed with the uploader's user id, and storage policies allowing each user to read, write, and delete only inside their own prefix.

Upload path: an edge function named request-upload checks the declared MIME type against an allow list of pdf, png, and jpeg, enforces a size ceiling, and returns a signed upload URL plus the final path. The browser uploads directly to storage with that URL, then calls a confirm step that inserts a row into a files table with name, size, mime, and path.

UI: a drop zone that also accepts click-to-browse, a per-file progress bar, image thumbnails after upload, a generic icon for pdfs, and a delete action that removes both the storage object and the files row.

Validation edge cases: a file renamed to fake its extension gets caught by checking actual content type at the confirm step, zero-byte files are rejected with a message, and duplicate names receive a suffix rather than overwriting.

Failure states: an expired signed URL triggers a fresh request automatically, a mid-upload network drop leaves a retry chip on the file card, and a scheduled function cleans storage objects that have no matching files row.

Acceptance: a signed-out call to request-upload returns 401, user A cannot fetch user B's file even with the raw path, an oversized file is refused before any bytes move, and deleting a file makes its old signed URL stop working.

Features 4 prompts

Add search so users can find {{resource}} records by typing.

Backend:
- Use Postgres full-text search in Supabase: a tsvector column on the {{resource}} table covering its name and description fields, a GIN index, and a trigger keeping the column current on insert and update. Do not fetch all rows and filter them in JavaScript.
- Sanitize the query text so punctuation or stray quotes cannot break the tsquery.

UI:
- A search input in the app header, focusable with the / key, showing a dropdown of the top 8 matches. Arrow keys move the highlight, Enter opens the record, Escape closes.
- Debounce keystrokes by roughly 300ms and discard out-of-order responses so a slow early request cannot overwrite a later query's results.
- A full results page at /search?q= listing all matches with matched terms highlighted, reached by pressing Enter with the input focused.

States and edge cases:
- Under 2 characters: show a hint, run no query.
- Zero matches: say so plainly, then offer close partial matches using an ilike fallback.
- Search must respect existing Row Level Security and never return rows the current user cannot already open.
- An empty q on the results page redirects home.

Match the current header and list styling; introduce no new visual language.

To verify: seed several records sharing a distinctive word, search it, confirm the dropdown and results page agree, then log in as a second user and confirm the first user's private rows never appear in their results.
Add an LLM chatbot to this app as a slide-over panel opened from a button in the header.

Backend: an edge function named chat that reads the provider API key from a Supabase secret, forwards the conversation, and streams the reply so text appears token by token in the panel. Keep the system prompt in a chat_settings table so I can edit it from an admin screen without redeploying.

Data model: conversations and messages tables keyed to user_id with row level security, storing role, content, and token counts when the provider returns them.

Behaviors: enter sends, shift-enter inserts a newline, a stop button aborts the stream, and the last twenty messages of the active conversation travel as context. New chat starts a fresh conversation, and a sidebar lists past conversations titled by their first message.

States: a disabled composer with an explanatory notice when the secret is missing, a retry link on provider timeouts, and a skeleton bubble while the first tokens arrive.

Styling: user messages right-aligned in a filled bubble, assistant messages left-aligned with a copy button, markdown rendered with proper code blocks.

Acceptance: replies stream rather than arriving in one block, refreshing mid-conversation restores history from the messages table, aborting a stream keeps the partial text in place, and a second signed-in user cannot read another user's conversations.
Add transactional email notifications to this app through a single edge function named send-email that talks to Resend using an API key stored as a Supabase secret.

Events to wire: a welcome email after signup confirmation, a notification when someone comments on a record you own, and a weekly digest sent by a scheduled function that skips users with nothing to report.

Preferences: a notification_settings table with a per-user boolean for each event type, defaulted on, editable from a settings screen with instant saves. Every email footer carries an unsubscribe link containing a signed token that flips the matching boolean without requiring login.

Reliability: log every attempt to an email_log table with recipient, template, status, and provider error text, retry a failed send once, and never let a failed email break the user-facing action that triggered it. Posting a comment must succeed even when the notification fails.

Templates: a shared header and footer, plain readable HTML with a single button per message, and a preview route under /admin/emails that renders each template with sample data.

States: the settings screen confirms each toggle save inline, and the admin preview shows a clear notice when the Resend secret is missing instead of rendering a blank page.

Acceptance: toggling comment emails off stops that type while the digest still arrives, the unsubscribe link works from a logged-out browser, email_log holds a row with status for every attempted send, and deleting a user stops all sends to that address.
Add CSV import and export for {{resource}} records to this app.

Import flow: an Import button on the {{resource}} list opens a dialog with a drop zone, the file parses in the browser, and a mapping step shows my CSV headers beside selects for each target column, with obvious names pre-matched. A preview table then shows the first rows with invalid cells highlighted and a count of rows that will be skipped.

Validation: required columns enforced before the confirm button enables, dates accepted in a few common formats and normalized to ISO, numbers stripped of currency symbols, and duplicate detection against an existing unique field with a choice to skip or update.

Write path: inserts batched through the Supabase client in chunks so large files do not stall, a progress bar advancing per batch, and an import_runs table recording file name, row counts, and a JSON list of skipped rows I can download afterward.

Export: an Export button that respects the currently applied filters and sort, writes the visible columns to a CSV named with today's date, quotes fields containing commas, and prefixes a BOM so spreadsheet apps detect the encoding.

Edge cases: a file with a BOM, quoted fields containing embedded newlines, a header-only file, and a completely empty file must each produce a clear message rather than a crash.

Acceptance: importing the exported file back produces zero new rows when update-on-duplicate is chosen, skipped rows appear in the downloadable report, and a malformed file never leaves partial data behind, either the batches complete or the report states exactly what landed.

Frontend 4 prompts

Build an analytics dashboard for an app that tracks {{resource}}.

Layout: fixed left sidebar (Overview, {{resource}}, Reports, Settings), a top bar with a date range picker (presets for last 7 days, last 30 days, and custom) and a user menu. Main area on a 12-column grid.

Overview page:
- Four stat cards: total {{resource}}, count added this period, percent change versus the prior period with a signed value, and a fourth card labeled Placeholder that I will define later.
- A line chart of daily counts across the selected range and a bar chart of the top five categories, both using Recharts inside responsive containers with explicit heights.
- A table of the 20 most recent records: sortable columns, a status badge, a row action menu, and pagination beyond 20 rows.

Data: create a Supabase table and seed roughly 90 days of plausible demo rows so every card and chart renders from real queries, not hardcoded arrays. Changing the date range must refetch and update every widget on the page.

States: skeleton loaders while queries run, an empty state with one line of explanation when the range has no data, and an inline retry button on query failure.

Styling: neutral grays with a single accent color, generous whitespace, no gradients. Works at 1280px wide; on mobile the grid collapses to one column and the sidebar becomes a drawer.

Skip auth for now. When finished, list which component reads from which table and column so I can point them at production data later.
Build a landing page for a developer tool called Relay that syncs environment variables across machines.

Sections in order: a hero with a one-line value statement, a subline, and a primary Get Early Access button, a three-step How It Works row, a feature grid of six cards titled with short verbs, a single quote block, and a closing call to action above a minimal footer.

Waitlist: the hero button scrolls to an email capture form that writes email, referrer, and created_at to a waitlist table in Supabase, blocks duplicate emails with an already-signed-up message backed by a unique constraint, and swaps the form for a success state on submit.

Behaviors: a sticky header that gains a bottom border after scrolling, smooth scrolling to section anchors, and a mobile menu that collapses the nav into a sheet.

States: inline validation for malformed emails before any insert, and a fallback message with a mailto link if the insert itself fails.

Styling: dark background, high-contrast type, one accent color used only on buttons and links, generous vertical spacing between sections, and monospace accents on the product name to match the developer audience. No stock illustrations.

Acceptance: the page renders at phone width with no horizontal scroll, submitting the same email twice produces the duplicate message and exactly one row, every image carries alt text, and heading levels descend from a single h1 without skipping.
Build a pricing page at /pricing with three tiers named Hobby, Team, and Scale.

Layout: three cards in a row on desktop, stacked at phone width, with the Team card visually promoted by a border, a badge, and slightly larger scale. Above the cards, a monthly and annual toggle rewrites prices in place and shows the annual saving as a small note, with no reload and no layout shift.

Content per card: the price, a one-line description of who the tier is for, a feature list where included items are checked and excluded items are dimmed rather than hidden, and a call-to-action button. Hobby routes to signup, Team routes to signup with a plan query parameter, and Scale opens a contact dialog that writes name, email, and message to a sales_leads table in Supabase.

Below the cards: a comparison table with feature rows and tier columns, a sticky first column when it scrolls horizontally at narrow widths, and a short FAQ accordion with five entries.

Edge cases: the toggle state persists in the URL hash so a shared link opens on annual, the contact dialog validates the email before insert and confirms the send, and keyboard users can operate both the toggle and the accordion.

Styling: prices large and baseline-aligned across cards so the tiers scan in one glance, a restrained palette with the accent reserved for the promoted tier and the buttons.

Acceptance: switching the toggle changes all three prices at once, the comparison table scrolls inside its own container rather than the page, and submitting the Scale dialog creates exactly one sales_leads row.
Build a data table screen for {{resource}} rows backed by Supabase.

Columns: name, status as a colored badge, owner, created_at shown as relative time with the absolute date in a tooltip, and a numeric amount column right-aligned.

Sorting: clicking a header cycles ascending, descending, then cleared, with a direction arrow in the header. Sorting runs server-side through the Supabase query so it holds past the first page, and the sorted column plus direction sync into the URL so a shared link reproduces the view.

Pagination: server-side, 25 rows per page, a footer showing the visible range against the total from a count query, and previous and next buttons disabled at the edges.

Extras: a column visibility dropdown persisted to localStorage, a checkbox column with select-all scoped to the current page, and bulk delete on selection behind a confirmation dialog naming the exact count.

States: a loading skeleton with the same row height so nothing jumps, an empty state with a create button when no rows exist, and an inline error row with a retry action when the query fails.

Styling: a sticky header row inside a scrollable container, compact density with comfortable line height, and badge colors that keep readable contrast in dark mode.

Acceptance: sorting by amount descending across pages surfaces the true top values, not the loaded page reordered, the URL restores sort and page on reload, select-all touches only the visible page, and deleting the last row of a page steps back one page instead of stranding an empty screen.

Refactor 1 prompt

Audit every screen in this project for accessibility and fix what you find, without changing the visual design more than necessary.

Keyboard: make each interactive element reachable and operable with tab plus enter or space, add a skip-to-content link as the first focusable element, and confirm shadcn dialogs and sheets trap focus while open and return it to the trigger on close.

Semantics: replace clickable divs with buttons or links, give every icon-only button an aria-label naming its action, associate every input with a label element, and structure each screen with main, nav, and one h1, with heading levels that never skip.

Announcements: connect form errors to their fields with aria-describedby, announce async results like saves and deletions through a polite live region, and name loading spinners for screen readers instead of leaving them silent.

Color and motion: fix any Tailwind text and background pairs that fall below readable contrast, stop using color alone to convey status by adding an icon or text, and wrap non-essential animation in a prefers-reduced-motion check.

Focus visibility: keep a visible focus ring on every interactive element, and never remove an outline without providing a replacement.

Acceptance: I can complete signup, the primary create and delete flows, and navigation using only the keyboard, every image has meaningful alt text or an empty alt when decorative, and no interactive control remains a bare div. List each file you changed and the rule it violated.

Testing 1 prompt

Audit this app for unhandled failures and add consistent error handling. Do not change feature behavior or visual design while doing it.

Cover these layers:
- React: an error boundary around routed page content that shows a plain fallback with a reload button instead of a white screen. The shell and navigation stay usable when one page throws.
- Supabase queries: supabase-js returns { data, error } rather than throwing, so check the error object on every select, insert, update, and delete. Reads render an inline error with a retry button in place of the data; writes show a toast with a short human message and preserve whatever the user typed.
- Forms: validate required fields and formats before submit with field-level messages; the database rejecting bad input must never be the only feedback.
- Edge functions: wrap handlers so unexpected exceptions return JSON with a 500 instead of an empty response, and log enough context to find the failure later.
- Network: word the message differently for offline or timeout versus a server error, and never surface a raw error object, stack trace, or Postgres error code to the user.

Conventions: one shared toast helper and one shared error-to-message mapper so wording stays consistent across the app.

When finished, list every file you changed with one line on what is now handled there, plus any call sites that still swallow errors silently so I can decide about those separately.

Prompting patterns that work in Lovable

Write it like a short PRD

Open with one sentence on what the product is, then labeled sections for pages, data model, behaviors, and states. A structured prompt turns into a structured build; a vague one turns into a guessed one you renovate message by message.

Name your tables and access rules

List the tables, their key columns, and who can read or write each one. Lovable generates the Supabase migration and Row Level Security policies directly from this text, so precision here is the difference between real multi-tenancy and client-side filtering.

One change per message

After the first build, request a single feature or fix at a time and check the preview before continuing. Bundled requests fail together, and you will not know which sentence caused the regression.

Demand the unhappy paths

Every feature request should say what happens with no data, while data loads, and when a query fails. Left unstated, Lovable ships the happy path and blank screens stand in for the rest.

Common mistakes

The one-sentence app

"Build me a CRM" produces a generic shell you then reshape over dozens of messages, which costs more than writing the spec would have. Spend five minutes listing pages, data model, and roles before your first prompt.

Bolting auth on late

Adding login after the app works means retrofitting protected routes and rewriting RLS policies across every table. Say up front which pages require a session, even if the auth UI comes later.

Debugging with vibes

"It's broken, fix it" makes Lovable guess, and its guesses often restyle things you liked. Paste the exact error message, name the page and the action that triggered it, and say what should have happened instead.

Prompts for other tools