Step-by-step guide to build a feature-rich Recipe app with Lovable, covering design, data storage, search, user profiles and deployment tips plus.

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
You can build a simple Recipe CRUD app fully inside Lovable using Chat Mode edits, Preview, and Publish. I recommend a front-end React app that uses Supabase (client-only) for storage; configure Supabase keys in Lovable Cloud Secrets. You’ll never run a terminal inside Lovable — use Chat Mode to create files, Preview to test, and Publish (or GitHub sync) to deploy. Below are exact Lovable prompts to paste into Chat Mode so Lovable edits files and wiring for you.
A small single-page React app (src/) that lists recipes, lets you add/edit/delete recipes, and stores data in Supabase. Front-end only: the Supabase JS client talks directly to your database using keys stored in Lovable Secrets.
We will use Chat Mode to create files and edits, Preview to run the app in the Lovable preview environment, Lovable Cloud Secrets UI to add SUPABASE_URL and SUPABASE_ANON\_KEY, and Publish to publish the site. No terminal needed. If you want server-side functions or to run migrations, you’ll use GitHub export/sync and run CLI outside Lovable (instructions flagged as outside Lovable).
Prompt 1 — Scaffold app and basic UI
Goal: Create a minimal React app that shows a recipe list and a form to add recipes.
Files to create/modify:
Acceptance criteria (done when…): App loads in Preview and shows a static “No recipes yet” message and an input form that accepts name & description and calls a placeholder addRecipe function.
Secrets/integrations: none yet.
Prompt text to paste into Chat Mode:
// Create files for a minimal React SPA
// index.html
// // minimal HTML with a div#root and script to load /src/main.jsx (Lovable Preview will serve it)
create file index.html with content:
<!doctype html>
<html>
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Recipes</title></head>
<body><div id="root"></div><script type="module" src="/src/main.jsx"></script></body>
</html>
// src/main.jsx
create file src/main.jsx with content:
// bootstraps React
import React from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.jsx';
const root = createRoot(document.getElementById('root'));
root.render(<App />);
// src/App.jsx
create file src/App.jsx with content:
import React, { useState } from 'react';
import RecipeList from './components/RecipeList.jsx';
import RecipeForm from './components/RecipeForm.jsx';
export default function App(){
const [recipes, setRecipes] = useState([]);
function addRecipe(r){ setRecipes([r, ...recipes]); }
return (
<div style={{padding:20,fontFamily:'sans-serif'}}>
<h1>Recipes</h1>
<RecipeForm onAdd={addRecipe}/>
<RecipeList recipes={recipes}/>
</div>
);
}
// src/components/RecipeForm.jsx
create file src/components/RecipeForm.jsx with content:
import React, {useState} from 'react';
export default function RecipeForm({onAdd}){
const [name,setName]=useState('');
const [desc,setDesc]=useState('');
function submit(e){ e.preventDefault(); if(!name) return; onAdd({id:Date.now(),name,desc}); setName(''); setDesc(''); }
return (
<form onSubmit={submit} style={{marginBottom:20}}>
<input placeholder="Recipe name" value={name} onChange={e=>setName(e.target.value)} />
<input placeholder="Description" value={desc} onChange={e=>setDesc(e.target.value)} />
<button type="submit">Add</button>
</form>
);
}
// src/components/RecipeList.jsx
create file src/components/RecipeList.jsx with content:
import React from 'react';
export default function RecipeList({recipes}){
if(recipes.length===0) return <div>No recipes yet</div>;
return (
<ul>
{recipes.map(r=>(
<li key={r.id}><strong>{r.name}</strong>: {r.desc}</li>
))}
</ul>
);
}
// package.json
create file package.json with content:
{
"name":"lovable-recipes",
"private":true,
"type":"module"
}
Prompt 2 — Add Supabase client wiring & Secrets
Goal: Wire Supabase client and replace the in-memory add/list with Supabase calls. Create a small data adapter file that reads environment from Lovable Secrets.
Files to modify/create:
Acceptance criteria: Preview shows recipes fetched from Supabase (or empty list). Adding via form inserts into Supabase and appears in the list.
Secrets/integrations: Add two Secrets in Lovable Cloud Secrets UI: SUPABASE_URL and SUPABASE_ANON\_KEY.
Prompt text to paste into Chat Mode:
// Add Supabase client adapter and wire App to use it
// src/supabase/client.js
create file src/supabase/client.js with content:
// load keys from environment (Lovable Secrets will map to process.env)
import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js/+esm';
const url = process.env.SUPABASE_URL;
const key = process.env.SUPABASE_ANON_KEY;
if(!url || !key) {
console.warn('Supabase keys missing. Add SUPABASE_URL and SUPABASE_ANON_KEY in Lovable Secrets.');
}
export const supabase = createClient(url || '', key || '');
export async function listRecipes(){
const { data, error } = await supabase.from('recipes').select('*').order('id',{ascending:false});
if(error) throw error;
return data || [];
}
export async function insertRecipe(r){
const { data, error } = await supabase.from('recipes').insert([{name:r.name,description:r.desc}]).select();
if(error) throw error;
return data[0];
}
// modify src/App.jsx to use these functions
update src/App.jsx replacing content with:
import React, { useEffect, useState } from 'react';
import RecipeList from './components/RecipeList.jsx';
import RecipeForm from './components/RecipeForm.jsx';
import { listRecipes, insertRecipe } from './supabase/client.js';
export default function App(){
const [recipes, setRecipes] = useState([]);
useEffect(()=>{ (async()=>{ try{ const rows = await listRecipes(); setRecipes(rows); }catch(e){ console.error(e); } })(); },[]);
async function addRecipe(r){
try{ const created = await insertRecipe(r); setRecipes([created, ...recipes]); }catch(e){ console.error(e); }
}
return (
<div style={{padding:20,fontFamily:'sans-serif'}}>
<h1>Recipes</h1>
<RecipeForm onAdd={addRecipe}/>
<RecipeList recipes={recipes}/>
</div>
);
}
Prompt 3 — Secrets setup instructions (Manual step in Lovable UI)
Goal: Add Supabase keys to Lovable Cloud Secrets so the Preview environment has them.
Files: none.
Acceptance criteria: process.env.SUPABASE_URL and process.env.SUPABASE_ANON\_KEY are available in Preview and Console logs show client initialized.
Secrets setup steps (to perform in Lovable Cloud UI):
Prompt text to paste into Chat Mode (to add a short README in repo reminding about Secrets):
// create README_SUPABASE.md
create file README_SUPABASE.md with content:
// Reminder: set these in Lovable Secrets
SUPABASE_URL — set in Lovable Cloud Secrets
SUPABASE_ANON_KEY — set in Lovable Cloud Secrets
```
This workflow uses only Lovable Chat Mode edits, Preview, Publish, and the Lovable Cloud Secrets UI. Anything requiring terminal work (database creation/migrations or custom build pipelines) must be handled via GitHub export/sync and executed outside Lovable; I flagged those steps above.
This prompt helps an AI assistant understand your setup and guide to build the feature
This prompt helps an AI assistant understand your setup and guide to build the feature
This prompt helps an AI assistant understand your setup and guide to build the feature

Book a call with an Expert
Starting a new venture? Need to upgrade your web app? RapidDev builds application with your growth in mind.
A recipe app built with Lovable should be organized for iterative, chat-driven edits: keep backend integrations (Supabase or similar) behind Secrets, use Lovable's Preview to validate UI changes, and export to GitHub only when you need CLI-level work (migrations, custom builds). Use small, focused AI code-generator prompts to produce components and API routes, always run them in Preview, and push to GitHub for anything requiring terminal commands.
// server/api/getRecipes.js
import { createClient } from '@supabase/supabase-js'
// initialize from Lovable Secrets mapped to process.env
const supabase = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_KEY)
export default async function handler(req, res) {
// GET /api/getRecipes
const { data, error } = await supabase.from('recipes').select('*')
if (error) return res.status(500).json({ error: error.message })
res.status(200).json(data)
}
From startups to enterprises and everything in between, see for yourself our incredible impact.
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.