Can AI build me a free meal planner app?
Yes. It can build a simple weekly planner where you drag in meals and get a combined grocery list, saved in your browser, instead of paying for a meal-planning app.
01Why bot
- A weekly grid plus a list generator is a small app.
- Browser storage keeps it free and private.
- Recipe imports and nutrition data are extra work.
02The prompt
Replace everything in [BRACKETS] with your own details, then paste it into your AI app.
You are a web developer. Build a weekly meal planner. Features: a Monday to Sunday grid, a saved list of my meals with ingredients, and an auto-combined grocery list grouped by store section. Requirements: - MUST be a single index.html file, mobile-first, saving data in the browser. - MUST combine duplicate ingredients on the grocery list. - Don't add features I didn't list. Then give me steps to publish it free and how to back up my data.
03Steps
- Run the prompt in an AI coding tool.
- Add 10 meals you make often.
- Plan a week and test the grocery list.
04What can go wrong
- Export your data now and then.
- Quantities may need manual tweaks.
05What to use
06Our test runs
2 runs, each with a different made-up sample. 0 of 2 passed every check.
The made-up sample:
This prompt has nothing to fill in.
- Provide a single index.html file
- Save data in the browser
- Combine duplicate ingredients on grocery list
- Include steps to back up data
- Didn't make up details we never gave it
What came back (excerpt):
DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.
Read the full answer
```html
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weekly Meal Planner</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
table {
border-collapse: collapse;
width: 100%;
}
th, td {
border: 1px solid #ddd;
padding: 10px;
text-align: left;
}
th {
background-color: #f0f0f0;
}
#grocery-list {
margin-top: 20px;
}
</style>
</head>
<body>
<h1>Weekly Meal Planner</h1>
<table id="meal-planner">
<tr>
<th>Monday</th>
<th>Tuesday</th>
<th>Wednesday</th>
<th>Thursday</th>
<th>Friday</th>
<th>Saturday</th>
<th>Sunday</th>
</tr>
<tr>
<td><input type="text" id="monday-meal" placeholder="Enter meal"></td>
<td><input type="text" id="tuesday-meal" placeholder="Enter meal"></td>
<td><input type="text" id="wednesday-meal" placeholder="Enter meal"></td>
<td><input type="text" id="thursday-meal" placeholder="Enter meal"></td>
<td><input type="text" id="friday-meal" placeholder="Enter meal"></td>
<td><input type="text" id="saturday-meal" placeholder="Enter meal"></td>
<td><input type="text" id="sunday-meal" placeholder="Enter meal"></td>
</tr>
<tr>
<td><textarea id="monday-ingredients" placeholder="Enter ingredients"></textarea></td>
<td><textarea id="tuesday-ingredients" placeholder="Enter ingredients"></textarea></td>
<td><textarea id="wednesday-ingredients" placeholder="Enter ingredients"></textarea></td>
<td><textarea id="thursday-ingredients" placeholder="Enter ingredients"></textarea></td>
<td><textarea id="friday-ingredients" placeholder="Enter ingredients"></textarea></td>
<td><textarea id="saturday-ingredients" placeholder="Enter ingredients"></textarea></td>
<td><textarea id="sunday-ingredients" placeholder="Enter ingredients"></textarea></td>
</tr>
</table>
<button id="save-meals">Save Meals</button>
<button id="generate-grocery-list">Generate Grocery List</button>
<div id="grocery-list"></div>
<script>
const meals = {};
const groceryList = {};
document.getElementById('save-meals').addEventListener('click', () => {
meals.monday = {
meal: document.getElementById('monday-meal').value,
ingredients: document.getElementById('monday-ingredients').value.split(',')
};
meals.tuesday = {
meal: document.getElementById('tuesday-meal').value,
ingredients: document.getElementById('tuesday-ingredients').value.split(',')
};
meals.wednesday = {
meal: document.getElementById('wednesday-meal').value,
ingredients: document.getElementById('wednesday-ingredients').value.split(',')
};
meals.thursday = {
meal: document.getElementById('thursday-meal').value,
ingredients: document.getElementById('thursday-ingredients').value.split(',')
};
meals.friday = {
meal: document.getElementById('friday-meal').value,
ingredients: document.getElementById('friday-ingredients').value.split(',')
};
meals.saturday = {
meal: document.getElementById('saturday-meal').value,
ingredients: document.getElementById('saturday-ingredients').value.split(',')
};
meals.sunday = {
meal: document.getElementById('sunday-meal').value,
ingredients: document.getElementById('sunday-ingredients').value.split(',')
};
localStorage.setItem('meals', JSON.stringify(meals));
});
document.getElementById('generate-grocery-list').addEventListener('click', () => {
const storedMeals = JSON.parse(localStorage.getItem('meals'));
if (storedMeals) {
Object.keys(storedMeals).forEach(day => {
storedMeals[day].ingredients.forEach(ingredient => {
const trimmedIngredient = ingredient.trim();
if (trimmedIngredient) {
if (groceryList[trimmedIngredient]) {
groceryList[trimmedIngredient].count++;
} else {
groceryList[trimmedIngredient] = {
count: 1,
section: getSection(trimmedIngredient)
};
}
}
});
});
const groceryListHtml = Object.keys(groceryList).map(ingredient => {
return `<p>${ingredient} (${groceryList[ingredient].section}) - ${groceryList[ingredient].count}</p>`;
}).join('');
document.getElementById('grocery-list').innerHTML = groceryListHtml;
}
});
function getSection(ingredient) {
const sections = {
'produce': ['apple', 'banana', 'carrot'],
'meat': ['chicken', 'beef', 'pork'],
'dairy': ['milk', 'eggs', 'cheese'],
'pantry': ['rice', 'pasta', 'canned goods']
};
for (const section in sections) {
if (sections[section].includes(ingredient.toLowerCase())) {
return section;
}
}
return 'unknown';
}
</script>
</body>
</html>
```
### Steps to Publish for Free:
1. **Create a GitHub account**: If you don't already have one, sign up for a free GitHub account.
2. **Create a new repository**: Click the "+" button in the top right corner of your GitHub dashboard and select "New repository".
3. **Upload your index.html file**: Drag and drop your `index.html` file into the repository, or use the "Upload files" button.
4. **Create a GitHub Pages site**: Go to your repository's settings, scroll down to the "GitHub Pages" section, and select "Master Branch" as the source.
5. **Wait for GitHub Pages to deploy**: This may take a few minutes. Once deployed, you'll see a message indicatingThe answer omits backup instructions and is incomplete.
The made-up sample (a messier one):
This prompt has nothing to fill in.
- Must be a single index.html file
- Must be mobile‑first
- Must combine duplicate ingredients on grocery list
- Must give steps to publish it free and back up data
- Didn't make up details we never gave it
What came back (excerpt):
DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.
Read the full answer
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Weekly Meal Planner</title>
<style>
body {
font-family: Arial, sans-serif;
}
table {
border-collapse: collapse;
}
th, td {
border: 1px solid #ddd;
padding: 10px;
}
.day {
width: 14%;
}
.meal {
width: 70%;
}
.ingredients {
width: 16%;
}
#grocery-list {
margin-top: 20px;
}
</style>
</head>
<body>
<h1>Weekly Meal Planner</h1>
<table id="meal-planner">
<tr>
<th class="day">Monday</th>
<td class="meal"><input type="text" id="monday-meal"></td>
<td class="ingredients"><input type="text" id="monday-ingredients"></td>
</tr>
<tr>
<th class="day">Tuesday</th>
<td class="meal"><input type="text" id="tuesday-meal"></td>
<td class="ingredients"><input type="text" id="tuesday-ingredients"></td>
</tr>
<tr>
<th class="day">Wednesday</th>
<td class="meal"><input type="text" id="wednesday-meal"></td>
<td class="ingredients"><input type="text" id="wednesday-ingredients"></td>
</tr>
<tr>
<th class="day">Thursday</th>
<td class="meal"><input type="text" id="thursday-meal"></td>
<td class="ingredients"><input type="text" id="thursday-ingredients"></td>
</tr>
<tr>
<th class="day">Friday</th>
<td class="meal"><input type="text" id="friday-meal"></td>
<td class="ingredients"><input type="text" id="friday-ingredients"></td>
</tr>
<tr>
<th class="day">Saturday</th>
<td class="meal"><input type="text" id="saturday-meal"></td>
<td class="ingredients"><input type="text" id="saturday-ingredients"></td>
</tr>
<tr>
<th class="day">Sunday</th>
<td class="meal"><input type="text" id="sunday-meal"></td>
<td class="ingredients"><input type="text" id="sunday-ingredients"></td>
</tr>
</table>
<button id="save-meals">Save Meals</button>
<button id="generate-grocery-list">Generate Grocery List</button>
<div id="grocery-list"></div>
<script>
const mealPlanner = document.getElementById('meal-planner');
const saveMealsButton = document.getElementById('save-meals');
const generateGroceryListButton = document.getElementById('generate-grocery-list');
const groceryListDiv = document.getElementById('grocery-list');
let meals = {};
let groceryList = {};
saveMealsButton.addEventListener('click', () => {
const mondayMeal = document.getElementById('monday-meal').value;
const mondayIngredients = document.getElementById('monday-ingredients').value;
const tuesdayMeal = document.getElementById('tuesday-meal').value;
const tuesdayIngredients = document.getElementById('tuesday-ingredients').value;
const wednesdayMeal = document.getElementById('wednesday-meal').value;
const wednesdayIngredients = document.getElementById('wednesday-ingredients').value;
const thursdayMeal = document.getElementById('thursday-meal').value;
const thursdayIngredients = document.getElementById('thursday-ingredients').value;
const fridayMeal = document.getElementById('friday-meal').value;
const fridayIngredients = document.getElementById('friday-ingredients').value;
const saturdayMeal = document.getElementById('saturday-meal').value;
const saturdayIngredients = document.getElementById('saturday-ingredients').value;
const sundayMeal = document.getElementById('sunday-meal').value;
const sundayIngredients = document.getElementById('sunday-ingredients').value;
meals = {
monday: { meal: mondayMeal, ingredients: mondayIngredients },
tuesday: { meal: tuesdayMeal, ingredients: tuesdayIngredients },
wednesday: { meal: wednesdayMeal, ingredients: wednesdayIngredients },
thursday: { meal: thursdayMeal, ingredients: thursdayIngredients },
friday: { meal: fridayMeal, ingredients: fridayIngredients },
saturday: { meal: saturdayMeal, ingredients: saturdayIngredients },
sunday: { meal: sundayMeal, ingredients: sundayIngredients }
};
localStorage.setItem('meals', JSON.stringify(meals));
});
generateGroceryListButton.addEventListener('click', () => {
const storedMeals = localStorage.getItem('meals');
if (storedMeals) {
meals = JSON.parse(storedMeals);
}
groceryList = {};
for (const day in meals) {
const ingredients = meals[day].ingredients.split(',');
for (const ingredient of ingredients) {
const trimmedIngredient = ingredient.trim();
if (trimmedIngredient) {
if (groceryList[trimmedIngredient]) {
groceryList[trimmedIngredient]++;
} else {
groceryList[trimmedIngredient] = 1;
}
}
}
}
const groupedGroceryList = {};
for (const ingredient in groceryList) {
const section = getStoreSection(ingredient);
if (groupedGroceryList[section]) {
groupedGroceryList[section].push({ ingredient, quantity: groceryList[ingredient] });
} else {
groupedGroceryList[section] = [{ ingredient, quantity: groceryList[ingredient] }];
}
}
const groceryListHtml = [];
for (const section in groupedGroceryList) {
groceryListHtml.push(`<h2>${section}</h2>`);
groceryListHtml.push('<ul>');
for (const item of groupedGroceryList[section]) {
groceryListHtml.push(`<li>${item.ingredient} (x${item.quantity})</li>`);
}
groceryListHtml.push('</ul>');The answer is incomplete and missing publishing and backup steps.
How each run works: we fill the prompt with a made-up sample, run it once in Llama 3.3 70B, and GPT-OSS 120B grades the answer against the prompt's own rules. Word limits are counted by code, and every quote flagged as made up is checked against the full answer. Your results can differ. How we test.