Maintaining car brand assets across multiple websites

A dealer group site, a marketplace, an internal fleet tool: the same manufacturer logos, three codebases, three chances to drift. Point every site at one URL pattern instead of copying files between repositories, and turn a ticket per codebase per rebrand into no ticket at all.

See what it saves
three products, one slug

One brand slug, three codebases

Change the slug once. Every surface re-renders from the same source.

Dealer group site

group.example
BMW full

Authorised BMW retailer

New and approved used, three locations

dealer-group-web/GET https://motomarks.io/img/bmw?size=lg

Marketplace listing

marketplace.example
  • BMW badge2024 BMW · 12,400 miles£38,950
  • BMW badge2023 BMW · 21,900 miles£31,200
  • BMW badge2022 BMW · 34,100 miles£27,750
listings-frontend/GET https://motomarks.io/img/bmw?type=badge&size=sm

Fleet dashboard

fleet.internal.example
VehicleDriverStatus
BMW wordmarkLK23 XFD
M. OkaforIn service
BMW wordmarkLT24 RVB
S. LindqvistDue MOT
BMW wordmarkLM24 QPA
UnassignedAvailable
fleet-ops-dashboard/GET https://motomarks.io/img/bmw?type=wordmark&size=xs&aspect=height
Three products rendering one brand. Each surface requests the variant and size it needs from the same slug; none of them holds a logo file. Change the slug to re-render all three.

Leaselab: multiple platforms, 100 brands, one URL

Leaselab is this page in one sentence: a company operating across multiple platforms, maintaining more than 100 automotive brands, and paying for it in hours every week. Here is how their CTO described the before and after.
Company
Leaselab
Footprint
Multiple platforms
Library
More than 100 automotive brands
Before Motomarks
An extensive operational cost

What the team stopped doing

  1. 01

    Hours chasing updated brand assets

    The asset behind the URL is updated; the URL does not change

  2. 02

    Resizing logos by hand for each placement

    size= and type= are query parameters

  3. 03

    Applying one change across various systems

    Every platform reads the same URL, so one update reaches all of them

We run more than 100 brands across multiple platforms. Every copy of a logo folder was a liability: a different file name, a different crop, a different version. Pointing every codebase at one URL pattern removed a whole class of drift, along with the storage and build steps that came with it.
AC
Adrian Ciaschetti
CTO, Leaselab

What it changes for a multi-product business

Every extra codebase multiplies the cost of a logo change. Four costs that stop multiplying, and what each one replaces.
One rebrand, one change, every product
A manufacturer refresh used to be a ticket in each repository, scheduled against each team's sprint. Now it is a line in the changelog and a cache expiry. Nobody coordinates three deploys.
Replaces a ticket per repository per rebrand
No package release for a logo
A design-system package that contains files needs a version bump, release notes, and an upgrade pull request in every consumer. A package that contains a URL helper never releases because a logo changed.
Replaces a release plus an upgrade PR per consumer
One company in front of the customer
The same person sees your marketplace, your dealer site, and your emails in the same week. Mismatched badges read as three companies. One URL pattern reads as one.
Replaces a design QA sweep across products
Asset cost attributed per product
The usage dashboard breaks requests out by domain, so each product's share is visible rather than one team quietly carrying the whole library for everyone else.
Replaces one team funding every product's logo work

What drifts when each site keeps its own files

Five ways copied assets diverge, and the part of the URL pattern that pins each one down.
File names
logo_bmw_final2.png in one repository, bmw-badge.svg in another. Nobody is sure which one is current.
/img/bmw
Sizes and padding
One site trimmed the whitespace, another did not. Badges in a shared table sit at different visual weights.
size=sm&aspect=square
Rebrands
A manufacturer refreshes its mark. The marketplace updates; the fleet tool ships the old badge for another year.
Cache-Control: max-age=86400
Missing variants
The consumer site has the wordmark. The internal tool has a badge someone screenshotted in 2021.
type=full|badge|wordmark
Package versions
The logo package is bumped in two of three apps. The third is pinned to a release from last spring.
brandLogoUrl()

What copied logo folders cost across three codebases

The defaults describe a library of Leaselab's size spread across a consumer site, a marketplace, and an internal tool. Every change lands three times, each with its own review and deploy. Put in your own numbers and compare with a plan.

Leaselab maintains more than 100

Consumer site, marketplace, internal tool

Rebrands, new brands, corrections

Source, resize, convert, commit, deploy, check

Find, trim, export sizes and formats, once

Salary, benefits, and overhead per hour

Motomarks plan to compare

Your numbers; change any input. First year = brands × minutes per brand + changes × codebases × hours per change. Each year after drops the one-time build. The model counts engineering time only; it leaves out CDN hosting, storage, and the cost of shipping an out-of-date mark.

Coverage where drift costs the most

Marks that changed in recent years are exactly where copied files go stale. Each tile links to the brand's logo page; the changelog lists updates as they are published.

Integration in four steps

One helper, per-site parameters, per-product usage, and updates that arrive through the cache. The examples use TypeScript; the URLs are the same in any stack.
The helper is the package. It has never needed a release for a logo change, because it has never contained one.
AC
Adrian Ciaschetti
CTO, Leaselab
  1. 01

    Publish one URL helper

    The helper is the whole package. No binaries, no release when a logo changes, because the package never contained the logo. Each product installs it and passes the slug it already stores.

    packages/brand-logo/src/index.ts
    export type LogoType = "full" | "badge" | "wordmark";
    export type LogoSize = "xs" | "sm" | "md" | "lg" | "xl";
    
    export interface LogoOptions {
      type?: LogoType;
      size?: LogoSize;
      format?: "png" | "webp";
      aspect?: "square" | "height";
    }
    
    export function brandLogoUrl(slug: string, options: LogoOptions = {}) {
      const { type = "badge", size = "sm", format, aspect } = options;
      const params = new URLSearchParams({
        type,
        size,
        token: process.env.NEXT_PUBLIC_MOTOMARKS_KEY!,
      });
      if (format) params.set("format", format);
      if (aspect) params.set("aspect", aspect);
      return `https://motomarks.io/img/${slug}?${params}`;
    }
  2. 02

    Let each site choose a variant, not a file

    The consumer site wants the full logo at hero size. The marketplace wants a badge in a listing row. The fleet tool wants a 16px wordmark in a table. They agree on the slug and disagree on parameters, which is the right place to disagree.

    three repositories, one helper
    // dealer-group-web
    <img src={brandLogoUrl(slug, { type: "full", size: "lg" })} alt={`${name} logo`} />
    
    // listings-frontend
    <img src={brandLogoUrl(slug, { type: "badge", size: "sm" })} alt={`${name} badge`} />
    
    // fleet-ops-dashboard
    <img
      src={brandLogoUrl(slug, { type: "wordmark", size: "xs", aspect: "height" })}
      alt={`${name} wordmark`}
    />
  3. 03

    Give each product a view of its own usage

    The usage dashboard lists requests by domain. Browser requests are attributed from their Referer automatically. Server-side calls carry none, so name the product with the optional header to break them out.

    Create a separate key per product when you want to roll one without touching the others.

    Optional request header
    GET https://api.motomarks.io/brands/bmw
    Authorization: Bearer sk_live_...
    X-Motomarks-Referer: fleet.internal.example
  4. 04

    Let the update arrive through the cache

    When a published logo changes, every site picks it up on its next uncached request. Browsers hold the previous version for at most 24 hours. The public changelog lists brand additions, logo updates, aliases, and metadata changes, so the rebrand ticket becomes a line you read rather than a file you chase.

    Response headers
    HTTP/1.1 200 OK
    Content-Type: image/webp
    Cache-Control: public, max-age=86400, immutable

Questions before you integrate

Do we need one API key per site?

One publishable key can serve every site, and the usage dashboard attributes browser traffic by domain. Create separate keys when you want to roll one product's key without touching the others. On Pro and Enterprise, each key can carry its own hostname allowlist.

What happens when a manufacturer changes its logo?

The published asset behind the URL is updated and the URL stays the same. Browsers and intermediate caches hold the previous image for up to 24 hours (Cache-Control: max-age=86400). The change is listed in the public changelog.

Can we keep a copy of the logos in our design system package?

Keep the URL helper in the package, not the files. Temporary caching for performance is within the fair use policy and Pro includes self-hosting and caching permission; bulk downloading the library for redistribution is not permitted. A package that contains only the helper never needs a release when a logo changes.

How do we handle a product behind a login?

Publishable keys work the same behind authentication. On the Free plan the attribution link must be on a publicly reachable page, so place it on your public marketing site. Startup and Pro remove the requirement.

Our databases hold legacy make names. How do we map them to slugs?

Pull the published list from the JSON API and match on name once, storing the slug alongside each record. Keep a small override map for names the API does not match (regional spellings, merged brands) and re-run the sync on a schedule.

Is a paid plan cheaper than the folder we already have?

The folder is free; keeping it current across several repositories is not. Startup is $228 a year billed monthly and Pro is $588. One rebrand applied in three codebases, each with its own review and deploy, is usually more than three hours of engineering time on its own. Leaselab, running more than 100 brands across multiple platforms, described the hand-maintained version as an extensive operational cost. The model on this page runs the comparison with your numbers.

One key.
Every site.

Create a free key, publish the helper, and retire the logo folders one repository at a time. The next rebrand is a changelog entry, not a sprint item.

See the brand changelog

Free includes 1,000 requests per day with an attribution link. Startup and Pro remove attribution.