MDX & Content Systems
Why our content systems resolve images instead of trusting raw paths
Images create a strange boundary in content-driven sites. Writers want to say, "Use this image." The application needs to know where the file lives, whether it can be optimized, what its dimensions are, what happens if the reference is wrong, and whether the value is a local import, public path, or remote URL.
We solve that boundary with image resolution rather than scattering path logic through components.
A content file should not know the bundler
An MDX file should not need to understand how Next.js imports a StaticImageData object. The content may contain a human-friendly image key or a simple filename. A small resolver can map that reference to the correct imported asset or a public path.
One production site uses a registry of named location images. The content model asks for an image key, and the loader resolves that key to a statically imported image. Another resolver checks a registry first, accepts deliberate root-relative or remote paths, and safely converts bare filenames into a controlled public content path.
That is more than convenience. It keeps application mechanics out of the writing layer.
Why static imports are useful
A statically imported image gives Next.js useful information before runtime, including intrinsic dimensions. That helps the image component reserve space and choose responsive sources. It also makes broken imports visible during the build instead of turning them into a surprise 404 after deployment.
For important site assets, that is a strong guarantee.
Why we still support public paths
Not every content image needs a static import. Large editorial libraries may be easier to manage through a public asset directory. A resolver can support those paths while still centralizing the rules. The point is not to force one image strategy across every site. The point is to avoid letting each component invent its own.
Fallback behavior should be deliberate
A missing image reference can fail in several ways. The page can crash. The browser can show a broken image icon. The component can silently disappear. Or the system can fall back to a known asset. For some content types, a fallback is appropriate. A location page may have a safe generic hero. A case study may not, because using the wrong project image would be misleading.
The resolver is the right place to make that policy visible.
It also prevents accidental path leakage
Content copied between projects often contains old paths. If components accept any arbitrary string and pass it directly into an image element, stale folder structures can survive unnoticed. A resolver gives us one place to normalize separators, strip unwanted directories, encode filenames, or reject unsupported values.
This is especially useful when a project is rebuilt from an older codebase and the asset organization changes.
Why this is a performance decision
Image handling and content handling are connected. A controlled resolver makes it easier to keep content inside the optimized image pipeline. If the application knows whether an asset is local and has dimensions, it can make better loading decisions. That helps us enforce rules such as:
- known dimensions for important imagery
- responsive
sizes - modern formats
- selective priority loading
- stable aspect ratios
- local caching where appropriate
A raw string path cannot express all of that by itself.
Why this is an editorial decision
Writers should be able to change an approved image without editing React. A stable image key can make that possible. The content says which approved asset it wants. The registry says what that asset actually is. The component says how that class of image should render.
That separation gives each layer one job.
The tradeoff
A registry has to be maintained. If every image in a massive library requires a manual import and registry entry, the system becomes tedious. That is why some repos use static registries for important repeated assets and public paths for broader editorial content.
The correct balance depends on the size and behavior of the library.
Why we prefer the resolver pattern
Without a resolver, image logic tends to spread. One component checks for an absolute URL. Another assumes /images/. Another imports a fallback. Another accepts a filename. Six months later, content editors need to remember which page type expects which path format. A resolver turns that into an explicit content contract.
The content identifies the image. The resolver translates that into a safe application value. The component handles presentation. It is a small abstraction, but it prevents a surprisingly common category of content bugs.
From explanation to proof
Where this connects to the work
These guides show how our publishing architecture supports large indexed libraries without requiring a database-backed page builder.