BuildWithMatija
  1. Home
  2. Blog
  3. Payload
  4. Add a Payload Sidebar Logo: Complete 5-Step Quick Guide

Add a Payload Sidebar Logo: Complete 5-Step Quick Guide

Step-by-step Payload admin guide: use beforeNavLinks, register SidebarLogo, run generate:importmap, enable tenant logos.

1st August 2026·Updated on:10th August 2026··
Payload
Add a Payload Sidebar Logo: Complete 5-Step Quick Guide

Evaluating Payload CMS Implementation Costs?

Scope design, content structure, and migration hours to estimate a realistic production timeline and hosting setup.

Try the Cost EstimatorGet a Second Opinion

📚 Comprehensive Payload CMS Guides

Detailed Payload guides with field configuration examples, custom components, and workflow optimization tips to speed up your CMS development process.

No spam. Unsubscribe anytime.

📄View markdown version
0

Frequently Asked Questions

About the author

Matija Žiberna

Matija Žiberna

Full-stack developer, co-founder

AboutResume

Self-taught full-stack developer sharing lessons from building software and startups.

I'm Matija Žiberna, a self-taught full-stack developer and co-founder passionate about building products, writing clean code, and figuring out how to turn ideas into businesses. I write about web development with Next.js, lessons from entrepreneurship, and the journey of learning by doing. My goal is to provide value through code—whether it's through tools, content, or real-world software.

Contents

  • Why This Isn't the Same as the Login Logo
  • 1. Pick the Right Injection Slot
  • 2. Register the Component
  • 3. Write the Component
  • 4. Regenerate the Import Map
  • 5. Verify
  • Making the Logo Dynamic Per Tenant
  • Worked Example
  • Slot Reference
  • FAQ
  • Wrapping Up
On this page:
  • Why This Isn't the Same as the Login Logo
  • 1. Pick the Right Injection Slot
  • 2. Register the Component
  • 3. Write the Component
  • 4. Regenerate the Import Map
Build with Matija logo

Build with Matija

Senior-led B2B websites, applications, content systems, and digital infrastructure. Business-first, full-stack, AI-assisted, no handoffs.

Services

  • B2B Website Development
  • CMS Architecture Review & Platform Blueprint
  • Next.js + Payload Advisory
  • AI Integration & Implementation

Resources

  • CMS Hub
  • B2B Website Strategy
  • E-commerce Hub
  • Blog
  • Case Studies

Payload CMS

  • Payload CMS Developer
  • Payload CMS Migration
  • Payload CMS Demos
  • All Payload CMS Resources

Discuss your project

Planning a rebuild, migration, application, workflow change, or platform decision? Start with the business problem and the system behind it.

Book a discovery callContact me →
© 2026Build with Matija•All rights reserved•Privacy Policy•Terms of Service
BuildWithMatija
Get In Touch

Adding a logo above the nav links in Payload's Admin panel means registering a custom component in the beforeNavLinks slot, writing a small component that renders your logo, and regenerating the import map so Payload actually picks it up. This guide walks through the full setup, including the one step that trips up most people: forgetting to run generate:importmap after adding the component.

I ran into this while setting up multi-tenant branding on a client project, where each tenant needed its own logo in the sidebar instead of one static image. Getting the basic single-logo version working first made the tenant-aware version much easier to reason about, so that's the order this guide follows too.

Why This Isn't the Same as the Login Logo

Payload's admin.components.graphics.Logo and Icon options control the login screen and the collapsed-nav icon only. They don't touch the sidebar itself. If you've already set those and you're wondering why your logo isn't showing up above the nav links, that's why. The sidebar needs a separate component, injected through a different slot entirely.

1. Pick the Right Injection Slot

Payload's Nav component (from @payloadcms/next) renders custom components in a fixed order. There's no "insert between X and Y" API, only a set of named slots:

code
NavWrapper
├─ beforeNav        ← above everything, outside the scrollable nav (e.g. multi-tenant's own "Filter by Tenant" selector lives here)
├─ nav.nav__wrap
│  ├─ beforeNavLinks ← top of the scrollable sidebar, above collection/global links
│  ├─ (collection/global links)
│  └─ afterNavLinks  ← below collection/global links, above Logout
├─ settingsMenu       ← inside the gear/settings popup
└─ Nav                 ← replaces the ENTIRE sidebar (last resort)

For a logo, beforeNavLinks is almost always the slot you want. It sits at the top of the scrollable nav, it already inherits the sidebar's inline padding from .nav__scroll, and it doesn't require reimplementing anything Payload already renders for you.

2. Register the Component

Point admin.components.beforeNavLinks at your component's file path, followed by the named export:

ts
// File: payload.config.ts
export const adminConfig: Config["admin"] = {
  components: {
    beforeNavLinks: [
      "/path/to/components/SidebarLogo#SidebarLogo",
    ],
  },
  // ...
}

Payload resolves this path through a generated import map rather than a direct import. That detail matters for step 4, so keep it in mind.

3. Write the Component

A plain <img> is enough here unless you specifically need SVG-as-a-React-component behavior. Since the logo is static, either a server or client component works. Make it a client component only once it needs to react to something dynamic, like a selected tenant.

tsx
// File: src/payload/admin-components/SidebarLogo.tsx
import React from "react";

export function SidebarLogo() {
  return (
    <div className="sidebar-logo">
      {/* eslint-disable-next-line @next/next/no-img-element */}
      <img src="/your-logo.svg" alt="Your Brand" />
    </div>
  );
}

export default SidebarLogo;

Add matching CSS through your custom Admin stylesheet. You don't need to hand-roll horizontal padding here, since .nav__scroll already applies it to everything inside beforeNavLinks and afterNavLinks.

css
/* File: src/app/(payload)/custom.css */
.sidebar-logo {
  margin-bottom: 1.5rem; /* vertical spacing only */
}

.sidebar-logo img {
  display: block;
  height: auto;
  max-width: 160px; /* cap the size so a large source image doesn't blow out the sidebar */
}

4. Regenerate the Import Map

This is the step people miss. New custom Admin components aren't picked up automatically, even in dev. Payload resolves every admin.components.* path through a generated file, src/app/(payload)/admin/importMap.js in the Next.js App Router integration. Skip this step and you'll get a runtime error like:

code
getFromImportMap: PayloadComponent not found in importMap
{ key: "/path/to/components/SidebarLogo#SidebarLogo", ... }
"You may need to run the `payload generate:importmap` command..."

Run it once after adding, renaming, or moving any custom component:

bash
pnpm payload generate:importmap
# or: npx payload generate:importmap

This rewrites importMap.js to import and register your new component. Commit that file. It's generated, but the build depends on it directly, so it needs to stay in version control and stay in sync with your components.

5. Verify

Reload the Admin panel and confirm the logo renders above the nav links. Check it across the dashboard, a collection list view, a document edit view, and the collapsed or mobile nav if your project supports one. If the logo doesn't show up, check the browser console before assuming it's a rendering bug. A stale import map is the most common cause.

Making the Logo Dynamic Per Tenant

Once the static version works, swapping in a per-tenant logo just means replacing the <img> with a client component that reads whatever state drives the logo choice. Return null when there's nothing to show, so a missing tenant falls back to an empty slot instead of a broken image.

tsx
// File: src/payload/admin-components/SidebarLogo.tsx
"use client";

export function SidebarLogo() {
  const key = useSomeSelector(); // e.g. useTenantSelection()
  const logo = LOGO_BY_KEY[key];

  if (!logo) return null;

  return (
    <div className="sidebar-logo">
      {/* eslint-disable-next-line @next/next/no-img-element */}
      <img src={logo.src} alt={logo.alt} />
    </div>
  );
}

The tenant selection state itself, and the full multi-tenant theming pattern this connects to, are covered in the multi-tenant Admin theming guide.

Worked Example

  • Component: src/payload/admin-components/tenant-theme/TenantSidebarLogo.tsx
  • Registration: src/payload/config/admin.ts
  • Styles: src/app/(payload)/custom.css

Slot Reference

SlotPositionBest for
beforeNavLinksTop of scrollable nav, above collection/global linksLogo, brand mark, tenant switcher
afterNavLinksBottom of scrollable nav, above LogoutSecondary links, version info, support link
beforeNavOutside the scrollable nav entirelyFilters that should stay visible while scrolling, like a tenant selector
NavReplaces the entire sidebarFull custom nav (you lose everything Payload renders by default)

FAQ

Why isn't my admin.components.graphics.Logo setting affecting the sidebar? That option only controls the login screen and the collapsed-nav icon. The sidebar logo is a separate component, registered through beforeNavLinks.

Do I need a client component for a static logo? No. A server component works fine for a single static logo. Only switch to a client component once the logo needs to respond to state, such as the active tenant.

What happens if I forget to run generate:importmap? Payload throws a runtime error saying the component wasn't found in the import map, and the Admin panel won't render your logo. Run pnpm payload generate:importmap and reload.

Should I commit importMap.js? Yes. It's a generated file, but it's also what the build uses directly, so it has to stay in version control and in sync with your registered components.

Can I use an SVG file instead of a React component? Yes, a plain <img src="/your-logo.svg"> works the same as any other image format here. Only reach for SVG-as-a-React-component if you need to manipulate the SVG's internals at runtime.

Wrapping Up

Getting a logo into the Payload Admin sidebar comes down to picking beforeNavLinks as the injection slot, writing a small component, and regenerating the import map so Payload's generated registry knows the component exists. The same pattern extends cleanly to a per-tenant logo once you swap the static image for a component that reads tenant state.

Let me know in the comments if you have questions, and subscribe for more practical development guides.

Thanks, Matija