Customizing Layouts

The Postnomic Client SDKs ship with clean, responsive default styling that works out of the box. You can customize the appearance to match your application's brand by choosing a markup style, overridi...

Overview

The Postnomic Client SDKs ship with clean, responsive default styling that works out of the box. You can customize the appearance to match your application's brand by choosing a markup style, overriding theme tokens, choosing a blog layout, and replacing default views or components.

Markup Styles

PostnomicClientOptions.MarkupStyle decides which CSS classes the components emit. This is the single most important setting for theming:

Value What the components emit
PostnomicMarkupStyle.Bootstrap (default) Bootstrap 5 utility classes — card, col-lg-8, badge bg-secondary, and so on
PostnomicMarkupStyle.Semantic A small, stable set of pn-* classes with no framework dependency
builder.Services.AddPostnomicBlog(options =>
{
    options.BaseUrl = "https://api.postnomic.com";
    options.ApiKey = "pk_your_blog_key_here";
    options.BlogSlug = "my-dev-blog";
    options.MarkupStyle = PostnomicMarkupStyle.Semantic;
});

Use Bootstrap when your application already uses Bootstrap and you want the blog to inherit your theme's typography, colors, and spacing for free. Use Semantic when you want to theme the blog yourself without fighting Bootstrap's utility classes.

Theming the Semantic Style

In Semantic mode the SDK ships a stylesheet that covers every pn-* class. Link it from your layout:

<!-- ASP.NET Core -->
<link rel="stylesheet" href="_content/Postnomic.Client.AspNetCore/postnomic-blog.css" />

<!-- Blazor -->
<link rel="stylesheet" href="_content/Postnomic.Client.Blazor/postnomic-blog.css" />

That stylesheet is built entirely from --pn-* custom properties declared on .pn-blog. You theme by overriding the properties, not the classes — no class overrides are required, and none are needed to keep working across SDK versions:

.pn-blog {
    --pn-font: "Inter", system-ui, sans-serif;
    --pn-font-heading: "Georgia", serif;
    --pn-max-width: 900px;
    --pn-surface: #ffffff;
    --pn-surface-variant: #f6f7f9;
    --pn-text: #1a1a2e;
    --pn-text-muted: #6b7280;
    --pn-primary: #2a5bd7;
    --pn-on-primary: #ffffff;
    --pn-border: #e5e7eb;
    --pn-link: var(--pn-primary);
    --pn-radius: 12px;
    --pn-radius-lg: 18px;
}

The --pn-space-xs--pn-space-xl properties control the spacing scale and can be overridden the same way. Because the properties cascade, setting them on any ancestor of .pn-blog works too — which makes it easy to drive the blog from your application's existing design tokens.

The pn-* Classes

If you do want to target elements directly, these are the classes the Semantic style emits:

Area Classes
Root and chrome pn-blog, pn-header, pn-title, pn-lead, pn-layout, pn-main, pn-sidebar
Post cards pn-card, pn-card__media, pn-card__body, pn-post-title, pn-post-meta, pn-excerpt, pn-tag
Post detail pn-post-content
Sidebar widgets pn-widget, pn-widget__title, pn-searchbox, pn-filter-banner
Comments pn-comment, pn-comment-form, pn-field
Pagination pn-pagination, pn-page, pn-page--active, pn-page--disabled
States pn-loading, pn-empty
Layout variant pn-masonry

A handful of postnomic-* classes are emitted in both markup styles, because they carry behaviour rather than theme: postnomic-masonry (Bootstrap mode's masonry container), postnomic-post-content, postnomic-cover-image, postnomic-post-thumbnail, postnomic-comment, postnomic-comment-replies, postnomic-branding, and postnomic-sidebar-ad. These are the only postnomic-* hooks that exist.

Blog Layout Options

Postnomic supports two built-in blog layouts, configurable per blog through the dashboard under Settings → Blog Settings:

  • Default — A traditional single-column blog listing with posts displayed vertically. Ideal for text-heavy content.
  • Masonry — A multi-column grid layout where posts flow into available space, similar to Pinterest. Uses CSS column-count with break-inside: avoid for a compact, visual presentation.

The layout setting is stored on the blog entity and returned via the API as defaultLayout. The Client SDKs automatically apply the corresponding CSS layout when rendering the blog index.

Masonry Layout Customization

To adjust the masonry layout's column count for different screen sizes, override the container. In Bootstrap mode the container is .postnomic-masonry; in Semantic mode it is .pn-masonry:

.postnomic-masonry,
.pn-masonry {
    column-count: 1;
}

@media (min-width: 768px) {
    .postnomic-masonry,
    .pn-masonry {
        column-count: 2;
    }
}

@media (min-width: 1200px) {
    .postnomic-masonry,
    .pn-masonry {
        column-count: 3;
    }
}

Overriding Views (ASP.NET Core)

For the ASP.NET Core integration, you can override individual Razor Pages by creating pages at the matching Area path in your own project. The ASP.NET Core routing system will prioritize your pages over the package defaults:

/Areas/Blog/Pages/Index.cshtml       → Override blog listing
/Areas/Blog/Pages/Post.cshtml        → Override post detail
/Areas/Blog/Pages/Author.cshtml      → Override author profile

Inject IPostnomicBlogService into your custom pages to fetch data:

public class IndexModel : PageModel
{
    private readonly IPostnomicBlogService _blogService;

    public IndexModel(IPostnomicBlogService blogService)
    {
        _blogService = blogService;
    }

    public async Task OnGetAsync()
    {
        var posts = await _blogService.GetPostsAsync();
        // Custom rendering logic
    }
}

Customizing Components (Blazor)

The Blazor package ships no routable pages — you already author your own pages that wrap BlogPage, PostPage, and AuthorPage (see Blazor Integration), so "overriding" the defaults is simply a matter of changing what those pages render. To replace a section outright, drop the SDK component and call IPostnomicBlogService yourself:

@inject IPostnomicBlogService BlogService

@code {
    private PostnomicPagedResult<PostnomicPostSummary>? _posts;

    protected override async Task OnInitializedAsync()
        => _posts = await BlogService.GetPostsAsync();
}

Customizing Text

PostnomicClientOptions.UiStrings lets you override the built-in UI labels (button text, empty-state copy, and so on) without touching markup, which is often all that is needed to make the blog feel native to your application.

Was this article helpful?

Thank you for your feedback!