Munchable's logo is a bitten chocolate chip cookie. There is no PNG of it on the website, and no img tag anywhere on the landing page. The mark is drawn in SVG by a React component, which means it has to solve a problem a PNG does not have: SVG masks are referenced by id, ids are global to the document, and a logo appears more than once per page.

Load munchable.app and count. Five instances of the mark, at sizes 64, 26, 24, 24 and 22, so ten masks on one page, and every id unique:

_R_alb_ / _R_albH1_ _R_ad8qnnb_ / _R_ad8qnnbH1_ _R_2jadannb_ / _R_2jadannbH1_ _R_q2nnb_ / _R_q2nnbH1_ _R_1apb_ / _R_1apbH1_ 

Those are React's useId values. Hardcode the ids instead and the page still looks right in a lot of cases, which is what makes it a good bug.

The cookie is two circles minus three circles, done twice

The bite could be a path. It is not. The whole mark is circles:

<mask id={rim} maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="32"> <rect x="-2" y="-2" width="36" height="36" fill="#fff" /> <circle cx="22.2" cy="2.7" r="2.9" fill="#000" /> <circle cx="26.4" cy="5.6" r="3" fill="#000" /> <circle cx="29.3" cy="9.8" r="2.9" fill="#000" /> </mask> <mask id={dough} maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="32"> <rect x="-2" y="-2" width="36" height="36" fill="#fff" /> <circle cx="22.2" cy="2.7" r="4.2" fill="#000" /> <circle cx="26.4" cy="5.6" r="4.3" fill="#000" /> <circle cx="29.3" cy="9.8" r="4.2" fill="#000" /> </mask> <circle cx="16" cy="16" r="15" fill="#C6802F" mask={`url(#${rim})`} /> <circle cx="16" cy="16" r="13.6" fill="#E7AC5B" mask={`url(#${dough})`} /> 

A white rect makes the mask opaque everywhere, three black circles punch the scalloped bite out of the upper right, and the shape drawn through it loses exactly that bite. Two filled circles stack to make the cookie: a darker baked rim at radius 15, golden dough at 13.6, so the rim shows as a 1.4-unit border.

The load-bearing detail is that the two masks use the same three centres at different radii. The dough's bite is cut wider than the rim's, by about 2.5 units in the 100-unit master drawing. Subtract the same bite from both and the bite edge would be raw dough with no rim on it, so the cookie would look like a baked biscuit everywhere except the one place a bite actually exposes an edge. Cutting the dough deeper leaves rim colour along the scallops too, and the border reads as continuous around the entire silhouette.

Two masks, six circles, no path data, and the geometry is legible enough that somebody can nudge a chip without opening a vector editor.

Why the ids cannot be constants

The standalone file version uses fixed ids, and it is correct to:

<mask id="fav-rim" ...> <mask id="fav-dough" ...> 

That file is a whole document. One mask per id, nothing to collide with.

Inline SVG in HTML is a different world. Those masks join the host document's id namespace, and url(#fav-rim) resolves against the whole page. Render the component in the header and again in the footer and you get two elements with id="fav-rim", which is invalid HTML and resolves to whichever the browser saw first. The result is two logos that still render, because both masks happen to be identical, so nothing visibly breaks and the duplicate ids sit there until something makes them diverge: a second size variant, a different bite, a themed version.

So the component mints its own:

export function CookieMark({ size = 26 }: { size?: number }) { // Unique per instance so the header + footer marks don't collide on ids. const rim = useId(); const dough = useId(); 

useId is the right tool rather than a module-level counter because it is stable across server render and hydration. A counter gives the server one sequence and the client another, and you get a hydration mismatch on your logo, which is a genuinely stupid error message to debug.

One consequence worth stating: this is why the component carries 'use client'. Hooks are not available in a server component, so a mark that needs useId cannot be a server component, and a brand mark is now a client component for reasons that have nothing to do with interactivity.

The same hook needs sanitising in React Native

The phone app draws the identical mark through react-native-svg, and the ids need one extra step:

const uid = useId().replace(/[^a-zA-Z0-9]/g, ''); const rim = `rim${uid}`; const dough = `dough${uid}`; 

React's generated ids contain punctuation, and they are not guaranteed to be a shape every renderer accepts as an id. On the web the browser is forgiving; in a native SVG renderer you are handing a string to a different implementation of id resolution. Stripping to alphanumerics and prefixing a letter keeps the uniqueness and drops the assumption.

Four drawings of one cookie, on purpose

The mark exists four times in the repo, and the differences between them are deliberate:

  1. The master, assets/brand/mark.svg, on a 100-unit viewBox with seven chips in two brown tones. Its comment calls itself the single source of truth for the geometry.
  2. The favicon, app/icon.svg, on a 32-unit viewBox with four chips in one tone. Fewer and larger chips, because at 16 pixels seven chips in two shades is mud. This is optical simplification, not a port.
  3. The web component, which carries the favicon's simplified geometry because it renders at 26 pixels in the header.
  4. The native component, which carries the master geometry because it renders large on a phone.

A shared package would have collapsed these into one drawing with a prop, and the prop would have been "detail level", which is a design decision pretending to be an API.

iOS does not accept the SVG, so it rasterises the SVG

Home-screen icons on iOS ignore SVG. The usual answer is to export a PNG, commit it, and let it drift away from the vector six months later when someone adjusts the mark.

Instead the touch icon is generated from icon.svg itself:

export const size = { width: 180, height: 180 }; export const contentType = 'image/png'; export default function AppleIcon() { const cookie = readFileSync(join(process.cwd(), 'app', 'icon.svg')).toString('base64'); return new ImageResponse( <div style={{ /* cream background, centred */ }}> <img src={`data:image/svg+xml;base64,${cookie}`} width={140} height={140} alt="" /> </div>, size, ); } 

next/og reads the file off disk, inlines it as a data URL and rasterises it to a 180 by 180 PNG with the cream background iOS needs, since the master mark is transparent and a transparent home-screen icon turns into a black square. Editing the SVG changes the touch icon with no second asset to remember.

There is one img tag in that snippet, and it lives inside the image generator. The pages themselves still have none.

Check it yourself

  • munchable.app renders five marks and 55 SVG elements, and zero img tags. View source and look for the _R_ mask ids above.
  • munchable.app/icon.svg is the simplified four-chip drawing, served as a file, with the fixed ids it is allowed to have. It is 27 lines and you can read the whole bite out of it.
  • munchable.app/about is a plainer page carrying the same mark in the header and the footer, which is the two-instance case the hook exists for.

If you have an inline SVG component with a mask, a gradient, a filter or a clip path in it, that id is a bug waiting for a second instance. The fix is four characters long.