Deploy a Website with AWS Amplify
Build and publish a budget tracker with AWS Amplify Hosting.
Introduction
30 Second Summary
Small purchases are easy to forget when each one seems harmless on its own. By the end of a week, the total can look very different from what you expected.
In this project, you will turn one copy-and-paste HTML file into SpendWise, a student budget tracker for fictional financial data. You will preview it on your computer before publishing it as a public website with AWS Amplify Hosting.
What You'll Build
You will watch SpendWise recalculate a fictional allowance as expenses arrive on a public page that keeps its data after a refresh.
By the end of this project, you'll have:
- A live budget summary that updates the budget, spent, and remaining totals as soon as you enter fictional values.
- An interactive expense list where you can add fictional purchases across five categories or remove incorrect entries.
- A shareable public website whose fictional values survive a refresh in the same browser.
- Secret Mission: Add a category filter that changes the visible expenses without changing the saved data or summary totals.
Are there any prerequisites?
You need an AWS account plus a Windows computer with internet access. You will use Microsoft Edge for the final check.
A little JavaScript familiarity helps, but all application code is ready to copy and paste.
Before We Start
Before any hands-on work begins, let's commit to tracking fictional financial data in SpendWise. You'll also pin down why a public AWS-hosted URL is more useful than a preview that stays on your computer.
Set Up Your One-File Workspace
SpendWise fits inside one HTML file. A clear workspace lets you see each pasted change as soon as you save it.
Visual Studio Code gives you a simple editor. Live Preview turns the saved file into a local webpage without requiring a programming language or framework.
In this step, get ready to:
- Install Visual Studio Code on Windows.
- Install the Microsoft Live Preview extension at version 0.4.20.
- Create index.html inside a local spendwise folder.
Install Visual Studio Code
Visual Studio Code keeps your project file visible while you work. Start by checking whether it is already installed.
- Press the Windows key to open the Windows search bar.
- Type Visual Studio Code into the search bar.
- Press Enter.
- Choose the tab that matches what happens.
✔️ Visual Studio Code Opens
Visual Studio Code is already installed. Keep the editor open for the next setup task.
ⓧ Visual Studio Code Is Not Installed
- In Microsoft Edge, open the Visual Studio Code Windows setup page.
- Download the User setup for Windows.
- Run the downloaded installer.
- Keep the default choices in the setup wizard.
- Complete the installation.
- Press the Windows key to open the Windows search bar.
- Type Visual Studio Code into the search bar.
- Press Enter.
That takes care of the first setup hurdle: Visual Studio Code is installed and open.
Can't Open Visual Studio Code?
- Run the downloaded installer again if the setup window closed before completion.
- Search for Visual Studio Code again after the installation finishes.
Help me install and open Visual Studio Code on Windows.
Install Live Preview
Live Preview hosts your saved webpage on a local server. This gives you a quick feedback loop for every change to index.html.
- Select the Extensions icon in the left Activity Bar.
- Enter Live Preview in the Extensions search field.
- Select the Live Preview extension published by Microsoft.
- Compare the listed version with 0.4.20.
- Choose the tab that matches the extension details page.
✔️ I See the Required Version
Live Preview is installed at version 0.4.20. Your preview tool is ready.
ⓧ I See an Older Version
- Update Live Preview from its extension details page.
- Restart Visual Studio Code if the update requests it.
- Return to the extension details page.
- Confirm that the listed version is 0.4.20.
ⓧ Live Preview Is Not Installed
- Select Install on the extension details page.
- Wait for the installation to finish.
- Confirm that the listed version is 0.4.20.
Your editor can now turn a saved HTML file into a local webpage.
Live Preview Won't Install?
- Confirm that the selected extension is published by Microsoft.
- Restart Visual Studio Code if the installation remains stuck.
- Retry the installation from the extension details page.
Help me troubleshoot the Live Preview extension installation.
Create and preview index.html
A single-file workspace keeps the first test easy to inspect. The spendwise folder holds the webpage you preview now and deploy later.
- Click File in the Visual Studio Code menu bar.
- Select Open Folder....
- Choose Desktop in the folder picker.
- Select New Folder.
- Name the folder spendwise.
- Open the new spendwise folder.
- Confirm that you trust the folder if Visual Studio Code asks.
The Explorer sidebar now shows the empty spendwise folder.
- Select New File... in the Explorer sidebar.
- Enter index.html as the file name.
- Add the starter heading by pasting this code into index.html:
<h1>SpendWise setup complete</h1>
What Does This Code Do?
- The <h1> element marks the page's main heading.
- The heading gives you a clear visual signal that the saved file is loading.
- Save index.html by pressing Ctrl+S.
Before you open the preview, what do you expect the page to display?
- Right-click index.html in the Explorer sidebar.
- Select Show Preview.
You'll see SpendWise setup complete as a heading in the built-in preview. If the preview opens in Microsoft Edge, you'll see the same heading there.
Don't See the Starter Heading?
- Confirm that the file is named index.html.
- Rename index.html.txt to index.html if Windows added the extra extension.
- Save index.html before reopening the preview.
Help me fix my Live Preview page when the starter heading does not appear.
✔️ My Preview Works
Great. Double check that index.html is saved before moving on.
ⓧ I'd Like to Double Check the Full Code
Your complete index.html file should match this exactly.
<h1>SpendWise setup complete</h1>
Your one-file workspace is ready. Next, you'll replace the starter heading with the complete SpendWise budget tracker.
Paste and Test the Budget Tracker
Your starter heading proved that Live Preview can render index.html from the spendwise folder.
A single HTML file can hold the page structure. CSS can shape the complete dashboard inside that file.
JavaScript turns the controls into working forms. Browser local storage keeps fictional entries for the same preview. You will test whether this local result meets the public demo goal.
In this step, get ready to:
- Replace the starter heading with the complete SpendWise tracker.
- Test the tracker with a fictional budget and expense.
- Identify why the local preview cannot serve as a public link.
Replace the starter page
The complete tracker keeps its layout, styles, storage logic, and interactions in one file. Replacing the starter heading gives Live Preview everything it needs to run the app.
- Switch back to index.html in Visual Studio Code.
- Select the starter heading by pressing Ctrl+A.
- Use the full-file tab below as the replacement source.
macOS
Keep going once the starter heading has been replaced with the complete tracker.
Windows
Copy this complete file when you replace the starter heading in index.html.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SpendWise Student Budget Tracker</title>
<style>
:root { font-family: Arial, sans-serif; color: #172033; background: #f3f6fb; }
* { box-sizing: border-box; }
body { margin: 0; }
header { padding: 2.5rem 1rem 3.5rem; text-align: center; color: white; background: #2447d8; }
header h1 { margin: 0 0 .5rem; }
header p { margin: 0; opacity: .9; }
main { width: min(900px, 92%); margin: -2rem auto 3rem; }
.summary, .forms { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.forms { grid-template-columns: 1fr 1.4fr; margin-top: 1rem; }
.card, .panel { padding: 1.2rem; border-radius: 14px; background: white; box-shadow: 0 10px 28px #1f3b7a18; }
.card span { display: block; color: #667085; font-size: .9rem; }
.card strong { display: block; margin-top: .35rem; font-size: 1.55rem; }
.remaining { color: #087f5b; }
h2 { margin-top: 0; font-size: 1.15rem; }
label { display: grid; gap: .35rem; margin-bottom: .8rem; color: #475467; font-size: .9rem; }
input, select, button { width: 100%; padding: .75rem; border-radius: 9px; font: inherit; }
input, select { border: 1px solid #cfd6e4; background: white; }
button { border: 0; color: white; background: #2447d8; font-weight: 700; cursor: pointer; }
button:hover { background: #1834af; }
#status { min-height: 1.4rem; color: #475467; }
.expenses { margin-top: 1rem; }
#expenseList { display: grid; gap: .7rem; padding: 0; list-style: none; }
.expense { display: grid; grid-template-columns: 1fr auto auto; gap: .8rem; align-items: center; padding: .85rem; border: 1px solid #e4e8f0; border-radius: 10px; }
.expense small { display: block; margin-top: .2rem; color: #667085; }
.delete { width: auto; padding: .5rem .7rem; background: #b42318; }
#emptyState { color: #667085; }
@media (max-width: 700px) {
.summary, .forms { grid-template-columns: 1fr; }
.expense { grid-template-columns: 1fr auto; }
.expense .delete { grid-column: 1 / -1; }
}
</style>
</head>
<body>
<header>
<h1>SpendWise Student Budget Tracker</h1>
<p>Plan a fictional allowance and see where it goes.</p>
</header>
<main>
<section class="summary" aria-label="Budget summary">
<article class="card"><span>Budget</span><strong id="budgetTotal">$0.00</strong></article>
<article class="card"><span>Spent</span><strong id="spentTotal">$0.00</strong></article>
<article class="card"><span>Remaining</span><strong class="remaining" id="remainingTotal">$0.00</strong></article>
</section>
<section class="forms">
<form class="panel" id="budgetForm">
<h2>Set allowance</h2>
<label>Budget amount ($)
<input id="budgetAmount" type="number" min="0.01" step="0.01" required>
</label>
<button type="submit">Save budget</button>
</form>
<form class="panel" id="expenseForm">
<h2>Add an expense</h2>
<label>Description
<input id="description" maxlength="80" required>
</label>
<label>Amount ($)
<input id="expenseAmount" type="number" min="0.01" step="0.01" required>
</label>
<label>Category
<select id="category">
<option>Food</option>
<option>Transport</option>
<option>School</option>
<option>Entertainment</option>
<option>Other</option>
</select>
</label>
<button type="submit">Add expense</button>
</form>
</section>
<p id="status" role="status"></p>
<section class="panel expenses">
<h2>Recent expenses</h2>
<p id="emptyState">No expenses yet.</p>
<ul id="expenseList"></ul>
</section>
</main>
<script>
const STORAGE_KEY = "spendwiseState";
function loadState() {
const savedState = localStorage.getItem(STORAGE_KEY);
if (!savedState) return { budget: 0, expenses: [] };
try {
const parsedState = JSON.parse(savedState);
return {
budget: Number(parsedState.budget) || 0,
expenses: Array.isArray(parsedState.expenses) ? parsedState.expenses : []
};
} catch {
return { budget: 0, expenses: [] };
}
}
let state = loadState();
const budgetForm = document.querySelector("#budgetForm");
const expenseForm = document.querySelector("#expenseForm");
const expenseList = document.querySelector("#expenseList");
const emptyState = document.querySelector("#emptyState");
const statusMessage = document.querySelector("#status");
function money(value) {
return `$${Number(value).toFixed(2)}`;
}
function saveState(message) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
statusMessage.textContent = message;
}
function render() {
const spent = state.expenses.reduce((total, expense) => total + expense.amount, 0);
document.querySelector("#budgetTotal").textContent = money(state.budget);
document.querySelector("#spentTotal").textContent = money(spent);
document.querySelector("#remainingTotal").textContent = money(state.budget - spent);
expenseList.replaceChildren();
emptyState.hidden = state.expenses.length > 0;
for (const expense of state.expenses) {
const item = document.createElement("li");
item.className = "expense";
const details = document.createElement("div");
const name = document.createElement("strong");
name.textContent = expense.description;
const category = document.createElement("small");
category.textContent = expense.category;
details.append(name, category);
const amount = document.createElement("strong");
amount.textContent = money(expense.amount);
const deleteButton = document.createElement("button");
deleteButton.type = "button";
deleteButton.className = "delete";
deleteButton.textContent = "Remove";
deleteButton.dataset.id = expense.id;
item.append(details, amount, deleteButton);
expenseList.append(item);
}
}
budgetForm.addEventListener("submit", event => {
event.preventDefault();
state.budget = Number(document.querySelector("#budgetAmount").value);
saveState("Budget saved in this browser.");
budgetForm.reset();
render();
});
expenseForm.addEventListener("submit", event => {
event.preventDefault();
state.expenses.unshift({
id: String(Date.now()),
description: document.querySelector("#description").value.trim(),
amount: Number(document.querySelector("#expenseAmount").value),
category: document.querySelector("#category").value
});
saveState("Expense saved in this browser.");
expenseForm.reset();
render();
});
expenseList.addEventListener("click", event => {
if (!event.target.matches(".delete")) return;
state.expenses = state.expenses.filter(expense => expense.id !== event.target.dataset.id);
saveState("Expense removed.");
render();
});
render();
</script>
</body>
</html>
How does the tracker work?
The page structure creates the summary cards, budget form, expense form, and expense list. The styles turn those elements into a responsive dashboard.
The STORAGE_KEY constant names the browser record. loadState() restores the saved budget and expenses when the page loads.
The saveState() function stores each update. The render() function recalculates the cards and rebuilds the visible expense list.
The form event handlers update state when you save a budget or add an expense. The click handler removes the matching expense.
- Paste the complete code over the selected starter heading.
- Save index.html by pressing Ctrl+S.
Before you refresh the preview, what do you expect to replace the starter heading?
- Switch back to the Live Preview session from earlier.
- Refresh the preview.
You should see the blue SpendWise header above three summary cards. You should also see forms for setting an allowance and adding an expense.
Still see the starter heading?
Confirm that the active file is index.html inside the spendwise folder. Make sure the file no longer contains the starter heading.
Save the file before refreshing the existing preview.
help me find why Live Preview still shows my old heading
Add a fictional budget and expense
The two forms update the same in-memory state object. Each successful submission also writes that state to the browser before the dashboard renders again.
- Enter 100 in the Budget amount ($) field.
- Click Save budget.
Good start. The Budget card now shows $100.00 while the Remaining card shows $100.00.
- Enter Lunch in the Description field.
- Enter 12.50 in the Amount ($) field.
- Select Food from the Category menu.
- Click Add expense.
The Spent card now shows $12.50. The Remaining card shows $87.50.
The Recent expenses list shows Lunch with the Food category. This confirms that both forms update the same dashboard.
Do the totals look different?
Confirm that the budget is 100. Check that the expense amount is 12.50.
Remove any extra expense rows before adding Lunch again.
help me debug the SpendWise totals
Refresh and inspect the preview
Each site receives its own browser storage area. A refresh tests whether spendwiseState was saved for this local preview.
Before you refresh, do you think the budget and expense will remain visible?
- Refresh the Live Preview page.
You should still see a Budget of $100.00 and a Spent total of $12.50. The Remaining card should still show $87.50.
You should also still see the Lunch expense. This proves that the local preview can restore data saved in that browser.
Before you inspect the preview address, do you expect it to be a public AWS Amplify Hosting URL?
- Inspect the address shown by Live Preview.
You will see a local preview address. It is not a public Amplify URL that other people can open from their devices.
That shortfall is intentional. SpendWise works locally, but the project still needs managed hosting before you can share it.
That's the local test complete. Next, you will package the same file for a public Amplify deployment.
Publish SpendWise with Amplify Hosting
Your SpendWise tracker now calculates expenses correctly. Its local preview address only works on your computer, so you cannot share the demo through that address.
Packaging the existing index.html with Windows PowerShell gives Amplify a deployable ZIP file. AWS Amplify Hosting publishes that file at a public URL without a backend or Git workflow.
In this step, get ready to:
- Package the SpendWise tracker as spendwise.zip.
- Deploy the ZIP through an Amplify app named spendwise-budget-tracker.
- Test browser storage at the public Amplify URL.
Prepare for manual deployment
A manual Amplify deployment accepts a ZIP containing your website files. Your ZIP needs index.html at its top level so Amplify can serve the page.
- In Visual Studio Code from earlier, select View from the top menu.
- Select Terminal.
- Create spendwise.zip beside index.html by running this command:
Compress-Archive -Path .\index.html -DestinationPath .\spendwise.zip -Force
What Does This Command Do?
- Compress-Archive creates the ZIP package.
- -Path selects the current index.html file.
- -DestinationPath names the package spendwise.zip.
- -Force allows the command to overwrite an older ZIP during future deployments.
- Confirm the Explorer sidebar lists spendwise.zip beside index.html.
You should see both files inside the existing spendwise folder. Your tracker is now packaged for upload.
ZIP File Missing?
Check that the terminal path ends with the spendwise folder. Confirm that index.html is saved in that folder before running the command again.
If PowerShell still does not create the file, help me troubleshoot the SpendWise ZIP command.
✔️ Awesome, I've got everything!
Your saved index.html remains unchanged. The new spendwise.zip contains that complete tracker file.
ⓧ I'd like to double check the full code
Compare your current index.html with this complete file before uploading the ZIP.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>SpendWise Student Budget Tracker</title>
<style>
:root { font-family: Arial, sans-serif; color: #172033; background: #f3f6fb; }
* { box-sizing: border-box; }
body { margin: 0; }
header { padding: 2.5rem 1rem 3.5rem; text-align: center; color: white; background: #2447d8; }
header h1 { margin: 0 0 .5rem; }
header p { margin: 0; opacity: .9; }
main { width: min(900px, 92%); margin: -2rem auto 3rem; }
.summary, .forms { display: grid; grid-template-columns: repeat(3, 1fr); gap: 1rem; }
.forms { grid-template-columns: 1fr 1.4fr; margin-top: 1rem; }
.card, .panel { padding: 1.2rem; border-radius: 14px; background: white; box-shadow: 0 10px 28px #1f3b7a18; }
.card span { display: block; color: #667085; font-size: .9rem; }
.card strong { display: block; margin-top: .35rem; font-size: 1.55rem; }
.remaining { color: #087f5b; }
h2 { margin-top: 0; font-size: 1.15rem; }
label { display: grid; gap: .35rem; margin-bottom: .8rem; color: #475467; font-size: .9rem; }
input, select, button { width: 100%; padding: .75rem; border-radius: 9px; font: inherit; }
input, select { border: 1px solid #cfd6e4; background: white; }
button { border: 0; color: white; background: #2447d8; font-weight: 700; cursor: pointer; }
button:hover { background: #1834af; }
#status { min-height: 1.4rem; color: #475467; }
.expenses { margin-top: 1rem; }
#expenseList { display: grid; gap: .7rem; padding: 0; list-style: none; }
.expense { display: grid; grid-template-columns: 1fr auto auto; gap: .8rem; align-items: center; padding: .85rem; border: 1px solid #e4e8f0; border-radius: 10px; }
.expense small { display: block; margin-top: .2rem; color: #667085; }
.delete { width: auto; padding: .5rem .7rem; background: #b42318; }
#emptyState { color: #667085; }
@media (max-width: 700px) {
.summary, .forms { grid-template-columns: 1fr; }
.expense { grid-template-columns: 1fr auto; }
.expense .delete { grid-column: 1 / -1; }
}
</style>
</head>
<body>
<header>
<h1>SpendWise Student Budget Tracker</h1>
<p>Plan a fictional allowance and see where it goes.</p>
</header>
<main>
<section class="summary" aria-label="Budget summary">
<article class="card"><span>Budget</span><strong id="budgetTotal">$0.00</strong></article>
<article class="card"><span>Spent</span><strong id="spentTotal">$0.00</strong></article>
<article class="card"><span>Remaining</span><strong class="remaining" id="remainingTotal">$0.00</strong></article>
</section>
<section class="forms">
<form class="panel" id="budgetForm">
<h2>Set allowance</h2>
<label>Budget amount ($)
<input id="budgetAmount" type="number" min="0.01" step="0.01" required>
</label>
<button type="submit">Save budget</button>
</form>
<form class="panel" id="expenseForm">
<h2>Add an expense</h2>
<label>Description
<input id="description" maxlength="80" required>
</label>
<label>Amount ($)
<input id="expenseAmount" type="number" min="0.01" step="0.01" required>
</label>
<label>Category
<select id="category">
<option>Food</option>
<option>Transport</option>
<option>School</option>
<option>Entertainment</option>
<option>Other</option>
</select>
</label>
<button type="submit">Add expense</button>
</form>
</section>
<p id="status" role="status"></p>
<section class="panel expenses">
<h2>Recent expenses</h2>
<p id="emptyState">No expenses yet.</p>
<ul id="expenseList"></ul>
</section>
</main>
<script>
const STORAGE_KEY = "spendwiseState";
function loadState() {
const savedState = localStorage.getItem(STORAGE_KEY);
if (!savedState) return { budget: 0, expenses: [] };
try {
const parsedState = JSON.parse(savedState);
return {
budget: Number(parsedState.budget) || 0,
expenses: Array.isArray(parsedState.expenses) ? parsedState.expenses : []
};
} catch {
return { budget: 0, expenses: [] };
}
}
let state = loadState();
const budgetForm = document.querySelector("#budgetForm");
const expenseForm = document.querySelector("#expenseForm");
const expenseList = document.querySelector("#expenseList");
const emptyState = document.querySelector("#emptyState");
const statusMessage = document.querySelector("#status");
function money(value) {
return `$${Number(value).toFixed(2)}`;
}
function saveState(message) {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
statusMessage.textContent = message;
}
function render() {
const spent = state.expenses.reduce((total, expense) => total + expense.amount, 0);
document.querySelector("#budgetTotal").textContent = money(state.budget);
document.querySelector("#spentTotal").textContent = money(spent);
document.querySelector("#remainingTotal").textContent = money(state.budget - spent);
expenseList.replaceChildren();
emptyState.hidden = state.expenses.length > 0;
for (const expense of state.expenses) {
const item = document.createElement("li");
item.className = "expense";
const details = document.createElement("div");
const name = document.createElement("strong");
name.textContent = expense.description;
const category = document.createElement("small");
category.textContent = expense.category;
details.append(name, category);
const amount = document.createElement("strong");
amount.textContent = money(expense.amount);
const deleteButton = document.createElement("button");
deleteButton.type = "button";
deleteButton.className = "delete";
deleteButton.textContent = "Remove";
deleteButton.dataset.id = expense.id;
item.append(details, amount, deleteButton);
expenseList.append(item);
}
}
budgetForm.addEventListener("submit", event => {
event.preventDefault();
state.budget = Number(document.querySelector("#budgetAmount").value);
saveState("Budget saved in this browser.");
budgetForm.reset();
render();
});
expenseForm.addEventListener("submit", event => {
event.preventDefault();
state.expenses.unshift({
id: String(Date.now()),
description: document.querySelector("#description").value.trim(),
amount: Number(document.querySelector("#expenseAmount").value),
category: document.querySelector("#category").value
});
saveState("Expense saved in this browser.");
expenseForm.reset();
render();
});
expenseList.addEventListener("click", event => {
if (!event.target.matches(".delete")) return;
state.expenses = state.expenses.filter(expense => expense.id !== event.target.dataset.id);
saveState("Expense removed.");
render();
});
render();
</script>
</body>
</html>
Publish with Amplify Hosting
Publishing creates a public resource in your AWS account. Tutorial-scale use is expected to fit applicable AWS Free Tier allowances or credits, while AWS remains pay as you go outside those allowances.
Why Amplify Hosting?
Amplify Hosting accepts a ZIP through a managed deployment flow. This keeps your first public deployment focused on packaging the website.
Static website hosting with Amazon S3 adds public bucket configuration. Amplify handles the hosting workflow for this project.
- Press the Windows key to open the search bar.
- Type Microsoft Edge and press Enter to open Microsoft Edge.
- Sign in to the AWS Management Console with your AWS account.
- Open AWS Amplify in the AWS Management Console.
- Choose Create new app.
- Choose Deploy without Git.
- Choose Next.
- Enter spendwise-budget-tracker in the App name field.
- Enter production in the Branch name field.
- Select Drag and drop as the Method.
Why the ZIP Structure Matters
The packaging command places index.html at the root of spendwise.zip. Amplify can serve that file as the website entry page.
A ZIP containing the outer spendwise folder can produce an Access Denied page because the entry file sits one level too deep.
- Drag spendwise.zip into the upload area.
- Choose Save and deploy.
- Wait for the deployment to finish.
You should see a successful deployment for the production branch. Amplify also provides a public URL for your SpendWise app.
Deployment Failed?
Confirm that you uploaded spendwise.zip from the existing spendwise folder. Recreate the ZIP if you accidentally uploaded the folder itself.
If the deployment succeeds but the site shows Access Denied, help me check the structure of my Amplify deployment ZIP.
Prove browser storage works publicly
Each website address gets a separate browser storage area. The public Amplify site therefore starts empty even though your local preview already contains a budget and expense.
Use fictional values during this test. This version has no user accounts or protected cloud database.
- Record the generated public URL here: your public Amplify URL.
- Open your public Amplify URL in Microsoft Edge.
You should see the complete SpendWise dashboard with empty totals. This fresh state confirms that the public URL has its own storage area.
- Enter 100 in the Budget amount ($) field.
- Choose Save budget.
- Enter Lunch in the Description field.
- Enter 12.50 in the Amount ($) field.
- Select Food in the Category field.
- Choose Add expense.
Before you refresh the public site, do you think these values will disappear or stay in the dashboard?
- Refresh the public site in Microsoft Edge.
You should still see the Lunch expense. The Remaining total should still show $87.50 at the public Amplify URL.
That local-only limitation is solved. Your SpendWise tracker is now public while its fictional values persist inside each visitor's browser.
Values Disappeared After Refresh?
Confirm that you refreshed the same public Amplify URL where you entered the values. Opening the local preview or a different deployment uses another storage area.
If the same public URL still resets, help me troubleshoot browser storage on my deployed SpendWise site.
Secret mission
Filter Expenses by Category
Give visitors a faster way to focus on one type of spending. Add a category filter that changes the visible expense list while every saved expense still contributes to the totals.
Clean Up Your Resources
Clean Up Your Resources
Your public SpendWise site can remain available through AWS Amplify Hosting after the tutorial. AWS usage can incur charges when your account is outside applicable allowances or credits.
Choose whether to keep the demo available, pause new deployments, or remove every project resource.
Cost warning
Tutorial-scale use is expected to fit applicable AWS Free Tier allowances or credits. Eligibility depends on your account. Choose Delete if you do not want future hosting usage.
Eligible accounts receive up to 1,000 build minutes per month. Eligible accounts also receive up to 5 GB of storage per month.
Eligible accounts receive up to 15 GB of data transfer out per month. Outside those allowances, standard builds cost $0.01 per minute.
Storage costs $0.023 per GB per month. Data transfer out costs $0.15 per GB served.
Resources you used:
- The AWS Amplify Hosting app named spendwise-budget-tracker with its production branch.
- The local spendwise folder containing index.html plus spendwise.zip.
- Fictional budget data in browser local storage for the Live Preview address.
- Fictional budget data in a separate browser storage area for the public SpendWise URL.
Keep everything running
No action is needed. Choose this if you want to keep sharing the public SpendWise demo.
- Leave spendwise-budget-tracker deployed with its production branch.
- Keep the spendwise folder so you can update the app later.
- Keep using fictional data because this app has no accounts or protected cloud database.
- Monitor your AWS billing for usage outside your applicable allowances or credits.
Pause - I'll come back to this later
Pause new development while keeping the public demo available. The existing app remains public, so hosting usage can continue.
- Stop uploading new versions of spendwise.zip to the production branch.
- Close the local preview tab in Visual Studio Code.
- Keep the spendwise folder so your code remains available.
- Leave spendwise-budget-tracker deployed so the public URL continues working.
- Review your AWS billing before you resume development.
Delete - I don't want to use this again
Remove every SpendWise resource when you no longer want the demo. The public URL disappears after you delete the Amplify app.
Clearing browser data can feel fiddly because each address stores a separate copy.
- In Microsoft Edge, return to the local Live Preview address from earlier.
- Open the settings for the current site's stored data.
- Clear the stored data for the local preview address.
- Refresh the local preview.
You should see Budget, Spent, plus Remaining return to $0.00. You should also see no saved expenses.
- Return to the public Amplify URL from earlier.
- Open the settings for the current site's stored data.
- Clear the stored data for the public URL.
- Refresh the public SpendWise site.
You should see the public app return to an empty budget with no saved expenses.
Your browser storage areas are now empty. Deleting the hosted app prevents the public demo from generating future hosting usage.
- Return to the AWS Management Console from earlier.
- Open AWS Amplify Hosting.
- Select spendwise-budget-tracker.
- Choose App settings.
- Choose General settings.
- Choose Delete app.
- Enter delete.
- Choose Delete app.
You should no longer see spendwise-budget-tracker in Amplify Hosting. Its public URL should stop serving SpendWise.
- Switch back to Visual Studio Code from earlier.
- Close the Visual Studio Code window that contains spendwise.
- In Windows, return to the location where you created the spendwise folder.
- Select the spendwise folder.
- Press Delete on your keyboard.
You should no longer see the spendwise folder in that location. Your hosted app, local files, plus fictional browser data are now removed.
Nice Work!
Nice Work!
Excellent work! SpendWise is now a public budget tracker hosted with AWS Amplify Hosting.
You've learned how to:
- Built a responsive single-file budget tracker in index.html. Its JavaScript events refresh the visible summary after each form submission.
- Stored fictional budget data with browser local storage. Confirmed the saved values survive a refresh in the same browser.
- Packaged index.html inside spendwise.zip. Published the app at a public Amplify URL.
- Secret Mission: Added category filtering by using selectedCategory to derive visibleExpenses. Kept state.expenses as the source for every summary total.
Ready to quiz yourself?