It's Day 2 of the #12DaysOfSvelteApps! π
Yesterday, we managed the cost of the holidays with the Budget Tracker. Today, we're managing the chaos of the gifts themselves.
The App: Purchase Tracker
The Purchase Tracker is your digital shopping assistant. While generic note-taking apps are fine, they lack structure. This app treats every potential gift as an "Item" with properties like category, default currency, and unit measurements.
π Try the App Live
π» View the Code
The "Use Case": The Anti-Spoiler List π€«
We all have that one family member who snoops on the shared "To-Do" list or the fridge notepad.
Use the Purchase Tracker to create a "Gifts" category.
- Name: The person you are buying for (or the item code name).
- Description: The actual item detail (e.g., "Size M, Blue, from that one vintage store").
- Privacy: Since the app functions essentially as a local database, your surprise plans remain on your device (or secured in your account).
Tech Spotlight: Optimistic UI β‘
One of the best UX patterns we implemented here is Optimistic UI. When you create a new Item, we don't want the user to wait for the server to say "Okay." We want it to appear instantly.
In our Svelte component:
function handleLocalCreateItem(e: SubmitEvent) {
e.preventDefault();
// ... extract form data ...
// 1. Update the local state IMMEDIATELY
const newItem = addItem(name, category, description, unit, currency);
// The UI updates instantly because 'items' is a reactive Svelte state
// items.current = [...oldItems, newItem];
toast.success('Item created successfully');
}
If the user is logged in, we perform the server request in the background. If it fails, we roll back. But 99% of the time, the user experiences zero latency.
Dealing with Complex Lists
We used shadcn-svelte components to quickly scaffold a beautiful Table view.
<Table>
<TableHeader>
<TableRow>
<TableHead>Name</TableHead>
<TableHead>Category</TableHead>
<!-- ... -->
</TableRow>
</TableHeader>
<TableBody>
{#each items.current as item (item.id)}
<TableRow onclick={() => navigateToPurchases(item.id)}>
<TableCell>{item.name}</TableCell>
<TableCell>
<!-- Category Icon + Name lookup -->
{categories.find(c => c.id === item.category)?.name}
</TableCell>
</TableRow>
{/each}
</TableBody>
</Table>
Using Svelte's {#each key} block (the (item.id) part) is crucial here. It ensures that when we delete or reorder items, the DOM updates efficiently without weird rendering artifactsβa must-have for any list-heavy application.
Next Up
Now that we have a Budget and a Shopping List, we need to actually get things done. Join us tomorrow for Day 3.
Top comments (0)