Place images alongside the markdown that uses them
To keep an image next to the markdown that references it, drop it into a folder alongside the page under Content/. When the same file is needed from multiple pages — a logo, a cover photo, a shared diagram — put it in wwwroot/ instead so it has one canonical URL.
Assumptions
- An existing Pennington site with at least one markdown page (see Add your first markdown page if not).
- The target markdown file is known.
- The project has a
wwwroot/folder for shared static files (the default for Web SDK projects).
Where to put images
Colocated next to the markdown file
Drop the image into a folder alongside the page — typically an assets/ subfolder. MarkdownContentService walks the content tree and copies every non-markdown file to the same relative path in the output, so the image ships with the page automatically.
Reference it with a standard markdown image tag and a relative path:

MarkdownLinkResolver resolves the link against the source file's URL, so it renders correctly whether the page is served at /main/images-and-assets/ or under a locale prefix.
Shared in wwwroot/
When the same image is referenced from multiple pages, drop it into wwwroot/ so it has one canonical URL. UsePennington wires UseStaticFiles for wwwroot/, so wwwroot/shared.png is served at /shared.png.
Reference it with a leading slash so it resolves at the site root regardless of which page embeds it:

When deploying under a sub-path, BaseUrlHtmlRewriter prepends the base URL at response time — avoid hard-coding the sub-path.
Excluded subtrees
To keep a folder under Content/ out of the output, list it in MarkdownContentOptions.ExcludePaths when calling AddMarkdownContent<T>. The copy pass skips any relative path matched by ExcludePaths.
/// <summary>
/// Relative subpaths (from <see cref="ContentPath"/>) to skip during discovery
/// and content copying. Set this on a broad catch-all source to carve out a
/// subtree that is owned by a more specific markdown source registered nearby.
/// See <c>MarkdownContentServiceOptions.ExcludePaths</c> for matching semantics.
/// </summary>
public ImmutableArray<string> ExcludePaths { get; set; } = ImmutableArray<string>.Empty;
Verify
- Run
dotnet runand open the page that references the colocated image — the image renders inline. - Visit the shared-asset URL directly (for example,
/shared.png) — the file loads. - Run
dotnet run -- buildand confirm both files appear inoutput/at their original relative paths.
Related
- Pennington.Infrastructure.MarkdownContentOptions —
ContentPath,ExcludePaths,FilePattern. - Front matter key reference — when an image is set as a social card or cover image via front matter.
- Dev mode and build mode share one code path — how
GetContentToCopyAsyncfits the content pipeline.