Project
Filenames as a lookup table: a tiny trick for product builders
A product builder for made-to-order shelving displays, where a real photo updates with every choice. Over 1,000 photos are named after the options they show, so finding the right one is a simple lookup, with a fallback for combinations that don't have a photo.
- My role
- Full-stack developer
- Stack
- react
- vite
- tailwind
- laravel
Why this matters
Product builders, "customize your order" pages and preview screens are everywhere, and they all have this problem.
Without a system, you end up with a huge `if/else` tree mapping choices to images. It's fragile and miserable to update.
A naming convention lets the photographer and the developer agree on one rule, and the code stays tiny no matter how many photos there are.
Explain it like I’m five
- Naming convention: An agreed recipe for names, like labelling jars "flavour-size-date". If everyone follows it, you can find any jar without looking inside.
- Template string / template literal: A string with blanks you fill in using
${}. Mad Libs for code. - Lookup table / map / dictionary: An object you ask "what's stored under this name?" and it answers instantly, like a coat-check ticket.
import.meta.glob(Vite): "Hey bundler, grab every file in this folder that matches this pattern and give me a list of them." Saves you writing 1,000 import lines.- Bundler / build step: The tool that packs your code and images into files a browser can load.
- Fallback: Plan B (and C, and D) for when Plan A doesn't work.
- Placeholder image: The "photo coming soon" picture you show when there's nothing better.
.webp: An image format like JPG, just smaller.
Step 1:Agree on the naming recipe before anyone takes photos.
Decide the order of the options and a short code for each value. Write it down somewhere the photographer can see it.
{wood}_{height}_{shelves}shelves.webp
oak_tall_5shelves.webp
walnut_short_3shelves.webpTip:
Use short codes (oak, not Oak Veneer Premium) and keep the order fixed. The order is the recipe.
Step 2:Turn the customer's choices into a filename.
const WOOD_CODES = { 'Oak': 'oak', 'Walnut': 'walnut' }
function buildFilename(choices) {
const wood = WOOD_CODES[choices.wood]
const { height, shelves } = choices
// If anything is missing, there is no filename to build yet.
if (!wood || !height || !shelves) return null
return `${wood}_${height}_${shelves}shelves.webp`
}Step 3:Load every photo into a lookup table, once.
With Vite, import.meta.glob grabs a whole folder. We key the result by just the filename so lookups are simple.
const files = import.meta.glob('./photos/*.webp', { eager: true, import: 'default' })
// { './photos/oak_tall_5shelves.webp': '/assets/oak_tall_5shelves-a1b2.webp', ... }
// becomes
// { 'oak_tall_5shelves.webp': '/assets/oak_tall_5shelves-a1b2.webp', ... }
const photos = {}
for (const [path, url] of Object.entries(files)) {
photos[path.split('/').pop()] = url
}Step 4:Look it up and show it.
const filename = buildFilename(choices)
const src = (filename && photos[filename]) || placeholder<img src={src} alt="Preview of your bookshelf" />Step 5:Add a fallback ladder for the "in-between" moments.
Photos come in levels: the base unit, then base + doors, then base + doors + lights. Try the most specific level first and step down until something matches. That way the customer keeps seeing the last good photo instead of a placeholder the moment they tick "add doors" but haven't picked a door style yet.
function pickPhoto(choices) {
const levels = []
levels.push('base')
if (choices.doors) levels.push('doors')
if (choices.lights) levels.push('lights')
// Most specific first
for (const level of levels.reverse()) {
const name = buildFilenameFor(level, choices)
if (name && photos[name]) return photos[name]
}
return placeholder
}- Most specific photoWood + doors + lights
- Less specificWood + doors
- Base photoJust the wood
- PlaceholderPhoto coming soon
Step 6:Fill early gaps with sensible defaults, and be honest about it.
On step 1 the customer has only picked a wood, but you still want a photo. Borrow a "typical" value for anything not chosen yet, and show a small note so nobody thinks it's their exact build.
const DEFAULTS = { wood: 'Oak', height: 'tall', shelves: 5 }
const photoChoices = {
wood: choices.wood ?? DEFAULTS.wood,
height: choices.height ?? DEFAULTS.height,
shelves: choices.shelves ?? DEFAULTS.shelves,
}
const isExample = !choices.height || !choices.shelves{isExample && <p role="status">Example photo — may not match your exact choices yet.</p>}Step 7:(Optional) A tiny script to check what's missing.
Keep a list of every filename you expect, and have a Node script compare it with what's in the folder. It turns "are all the photos here?" from guesswork into a number.
What could go wrong
- Spreading defaults the wrong way round.
{ ...DEFAULTS, ...choices }looks right, but ifchoices.heightisnull,nullwins and wipes out the default. Use??per field (like step 6), or strip out empty values first. - Case sensitivity.
Oak_Tall.webpandoak_tall.webpcan both "work" on your Windows or Mac laptop, then break on a Linux server. Pick lowercase (or uppercase) and stick to it. - Impossible combinations. Some combos can't physically exist, so a photo will never come. Either stop people choosing them (clear the invalid follow-up answer when an earlier one changes) or make sure the fallback ladder handles them gracefully.
- Options that aren't in the photo. If one choice (a material, a finish) was never photographed, the image won't change when it's picked. Say so on screen rather than letting people think the app is broken.
- Combinations multiply fast. 3 woods × 3 heights × 4 shelf counts × 3 extras is already 108 photos. Count the combinations before the photoshoot is booked.
- Missing files fail silently. A typo in one filename just shows the fallback. Log a warning in development (
if (import.meta.env.DEV) console.warn(...)) so you notice. - Bundle size. Hundreds of images bundled with the app increase build time and repo size. That's fine at this scale, but past a few thousand it's worth moving them to a CDN.
What I’d tell past me
- Lock the naming recipe in writing with whoever makes the images before the first batch arrives. Renaming 1,000 files later is no fun.
- Write the "what's missing?" script on day one, not when the third batch of photos lands.
- Design the fallback ladder early. "What do we show while they're choosing?" turned out to be a bigger question than "what do we show at the end?"
- An honest "this is an example photo" note beats a perfect-looking preview that's quietly wrong.