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:
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.
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-logoimg {
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:
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";
exportfunctionSidebarLogo() {
const key = useSomeSelector(); // e.g. useTenantSelection()const logo = LOGO_BY_KEY[key];
if (!logo) returnnull;
return (
<divclassName="sidebar-logo">
{/* eslint-disable-next-line @next/next/no-img-element */}
<imgsrc={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.
Top of scrollable nav, above collection/global links
Logo, brand mark, tenant switcher
afterNavLinks
Bottom of scrollable nav, above Logout
Secondary links, version info, support link
beforeNav
Outside the scrollable nav entirely
Filters that should stay visible while scrolling, like a tenant selector
Nav
Replaces the entire sidebar
Full 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.