Brand Asset Library
Last updated
Context
My long-time pharma aesthetics client again. Six product brands. Lots of media — images, video, social posts, before-and-after shots, in-office materials.

Two audiences:
- Brand managers — add content without calling a developer
- Field teams and HCPs — healthcare professionals find and download what they need, no training
Why WordPress for the backend
The team already knew WordPress. Non-technical staff could add a resource, attach files, pick a brand and media type, and publish — no code.
Two taxonomies:
- Brands — six product lines
- Media Types — Banner, Before and After, Celebrity, Image, In-Office Material, Social Media, Video
The front-end
React, React Router, React Bootstrap, Vite.
A useResources hook fetches from the WordPress REST API, applies filters, and keeps results in sync as you move between brands and media types.
Filtering:
- Pick a brand — narrows to one product line
- Toggle media types — refines within that brand
- Sort by most recent or most downloaded
The REST API is fast enough that it feels native even with bigger result sets. I also found a wordpress plugin which caches the responses to make it even speedier.
The download
Each tile has a download button. One resource can have multiple files — image, PDF spec sheet, video.
I made a custom REST route in a WordPress plugin. Front-end sends the resource post ID. The plugin:
- Retrieves all media attached to that post
- Fetches each file from the filesystem
- Assembles a
ZipArchivein PHP - Streams the zip to the browser with the correct
Content-TypeandContent-Dispositionheaders - Increments a download count field in the post's meta
$media_ids = get_post_meta($post_id, 'resource_files', true);
foreach ($media_ids as $media_id) {
$file_path = get_attached_file($media_id);
$zip->addFile($file_path, basename($file_path));
}Server-side streaming = normal file download. One request, one response.
What I'd do differently
Download count increments in the same request as the zip. Failed or interrupted downloads still bump the count.
Better: a separate lightweight endpoint the front-end hits only after the download finishes. Fine for rough internal usage tracking. Not fine if you need accurate analytics.
That being said, we did add Google Analytics to track downloads and other events, and they ended up getting rid of the front-facing count tally.
Bonus! Demo
So when I was bored the other day, I updated this asset repo to instead contain a fictional beer collective.
One day I shall discover the elusive 403 Forbidden Lager. Cheers! 🍻
