Centered Logo Navbar
Original · freenavbar with centred logo and split links
Copy prompt gives Claude Code, Cursor or v0 a ready-to-paste prompt that recreates this exact component.
npx shadcn@latest add https://webinnoventix.com/r/navx-centered-logo.json"use client";
import { useEffect, useId, useRef, useState } from "react";
import type { ReactElement } from "react";
import { AnimatePresence, motion, useReducedMotion } from "motion/react";
type SubItem = {
label: string;
description: string;
href: string;
icon: () => ReactElement;
};
type NavLink = {
label: string;
href: string;
};
function IconRealtime(): ReactElement {
return (
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="h-5 w-5">
<path
d="M3 12h3l2.5-6 4 12 2.5-6H21"
stroke="currentColor"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
);
}
function IconCohort(): ReactElement {
return (
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="h-5 w-5">
<circle cx="8" cy="8" r="3.25" stroke="currentColor" strokeWidth="1.75" />
<circle cx="16" cy="16" r="3.25" stroke="currentColor" strokeWidth="1.75" />
<path d="M10.4 10.4 13.6 13.6" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" />
</svg>
);
}
function IconAlert(): ReactElement {
return (
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="h-5 w-5">
<path
d="M12 4c-2.5 0-4 2-4 4.5 0 3-1.5 4-2.5 5.5h13c-1-1.5-2.5-2.5-2.5-5.5C16 6 14.5 4 12 4Z"
stroke="currentColor"
strokeWidth="1.75"
strokeLinejoin="round"
/>
<path d="M10 18a2 2 0 0 0 4 0" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" />
</svg>
);
}
function IconSync(): ReactElement {
return (
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="h-5 w-5">
<path
d="M5 8a7 7 0 0 1 11.5-2.5L19 8M19 16a7 7 0 0 1-11.5 2.5L5 16"
stroke="currentColor"
strokeWidth="1.75"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path d="M19 4v4h-4M5 20v-4h4" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
}
const productMenu: SubItem[] = [
{
label: "Realtime dashboards",
description: "Streaming metrics with sub-second refresh and shareable views.",
href: "#realtime",
icon: IconRealtime,
},
{
label: "Cohort analysis",
description: "Group users by behavior and track retention across releases.",
href: "#cohorts",
icon: IconCohort,
},
{
label: "Alerts & anomalies",
description: "Detect drift automatically and route to Slack or PagerDuty.",
href: "#alerts",
icon: IconAlert,
},
{
label: "Warehouse sync",
description: "Two-way sync with Snowflake, BigQuery, and Postgres.",
href: "#warehouse",
icon: IconSync,
},
];
const leftLinks: NavLink[] = [
{ label: "Pricing", href: "#pricing" },
{ label: "Customers", href: "#customers" },
];
const rightLinks: NavLink[] = [
{ label: "Docs", href: "#docs" },
{ label: "Changelog", href: "#changelog" },
{ label: "Sign in", href: "#signin" },
];
export default function NavxCenteredLogo(): ReactElement {
const reduce = useReducedMotion();
const rawId = useId();
const uid = rawId.replace(/[:]/g, "");
const menuId = `${uid}-product`;
const mobileId = `${uid}-mobile`;
const [scrolled, setScrolled] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
const [mobileOpen, setMobileOpen] = useState(false);
const sentinelRef = useRef<HTMLDivElement | null>(null);
const menuWrapRef = useRef<HTMLDivElement | null>(null);
const menuButtonRef = useRef<HTMLButtonElement | null>(null);
const mobileButtonRef = useRef<HTMLButtonElement | null>(null);
useEffect(() => {
const node = sentinelRef.current;
if (!node) return;
const observer = new IntersectionObserver(
(entries) => {
const first = entries[0];
if (first) setScrolled(!first.isIntersecting);
},
{ rootMargin: "0px", threshold: 0 },
);
observer.observe(node);
return () => observer.disconnect();
}, []);
useEffect(() => {
if (!menuOpen) return;
function onPointer(event: MouseEvent) {
const wrap = menuWrapRef.current;
if (wrap && event.target instanceof Node && !wrap.contains(event.target)) {
setMenuOpen(false);
}
}
document.addEventListener("mousedown", onPointer);
return () => document.removeEventListener("mousedown", onPointer);
}, [menuOpen]);
useEffect(() => {
function onKey(event: KeyboardEvent) {
if (event.key !== "Escape") return;
if (menuOpen) {
setMenuOpen(false);
menuButtonRef.current?.focus();
}
if (mobileOpen) {
setMobileOpen(false);
mobileButtonRef.current?.focus();
}
}
document.addEventListener("keydown", onKey);
return () => document.removeEventListener("keydown", onKey);
}, [menuOpen, mobileOpen]);
const linkClass =
"rounded-md px-2 py-1.5 text-sm font-medium text-slate-600 transition-colors hover:text-slate-950 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:text-slate-300 dark:hover:text-white dark:focus-visible:ring-offset-slate-950";
return (
<section className="relative w-full bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100">
<style>{`
@keyframes navx_live_pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.35; transform: scale(0.72); }
}
@keyframes navx_mark_drift {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-1.5px); }
}
.navx-live-dot { animation: navx_live_pulse 2s ease-in-out infinite; }
.navx-mark { animation: navx_mark_drift 5s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) {
.navx-live-dot, .navx-mark { animation: none !important; }
}
`}</style>
<div ref={sentinelRef} aria-hidden="true" className="absolute inset-x-0 top-0 h-px" />
<header
className={`sticky top-0 z-40 w-full border-b transition-colors duration-300 ${
scrolled || menuOpen
? "border-slate-200/80 bg-white/85 backdrop-blur-xl dark:border-slate-800/80 dark:bg-slate-950/80"
: "border-transparent bg-transparent"
}`}
>
<nav aria-label="Primary" className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="grid h-16 grid-cols-[1fr_auto_1fr] items-center gap-4 lg:h-20">
{/* Left region: hamburger (mobile) + split links (desktop) */}
<div className="flex items-center justify-start gap-1">
<button
ref={mobileButtonRef}
type="button"
onClick={() => setMobileOpen((v) => !v)}
aria-expanded={mobileOpen}
aria-controls={mobileId}
aria-label={mobileOpen ? "Close menu" : "Open menu"}
className="inline-flex h-10 w-10 items-center justify-center rounded-lg text-slate-700 transition-colors hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white lg:hidden dark:text-slate-200 dark:hover:bg-slate-800 dark:focus-visible:ring-offset-slate-950"
>
{mobileOpen ? (
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="h-5 w-5">
<path d="m6 6 12 12M18 6 6 18" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" />
</svg>
) : (
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="h-5 w-5">
<path d="M4 7h16M4 12h16M4 17h16" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" />
</svg>
)}
</button>
<div className="hidden items-center gap-1 lg:flex" ref={menuWrapRef}>
<div className="relative">
<button
ref={menuButtonRef}
type="button"
onClick={() => setMenuOpen((v) => !v)}
aria-expanded={menuOpen}
aria-controls={menuId}
aria-haspopup="true"
className="inline-flex items-center gap-1 rounded-md px-2 py-1.5 text-sm font-medium text-slate-600 transition-colors hover:text-slate-950 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:text-slate-300 dark:hover:text-white dark:focus-visible:ring-offset-slate-950"
>
Platform
<motion.span
aria-hidden="true"
animate={{ rotate: menuOpen ? 180 : 0 }}
transition={reduce ? { duration: 0 } : { type: "spring", stiffness: 400, damping: 30 }}
className="inline-flex"
>
<svg viewBox="0 0 24 24" fill="none" className="h-4 w-4">
<path d="m6 9 6 6 6-6" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</motion.span>
</button>
<AnimatePresence>
{menuOpen ? (
<motion.div
key="product-menu"
id={menuId}
role="region"
aria-label="Platform"
initial={reduce ? { opacity: 0 } : { opacity: 0, y: 8, scale: 0.98 }}
animate={reduce ? { opacity: 1 } : { opacity: 1, y: 0, scale: 1 }}
exit={reduce ? { opacity: 0 } : { opacity: 0, y: 6, scale: 0.98 }}
transition={reduce ? { duration: 0 } : { duration: 0.18, ease: [0.22, 1, 0.36, 1] }}
className="absolute left-0 top-full z-50 mt-3 w-[26rem] origin-top-left rounded-2xl border border-slate-200 bg-white p-2 shadow-2xl shadow-slate-900/10 dark:border-slate-800 dark:bg-slate-900 dark:shadow-black/40"
>
<ul className="grid gap-1">
{productMenu.map((item) => {
const Icon = item.icon;
return (
<li key={item.label}>
<a
href={item.href}
onClick={() => setMenuOpen(false)}
className="group flex items-start gap-3 rounded-xl p-3 transition-colors hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:hover:bg-slate-800/70 dark:focus-visible:ring-offset-slate-900"
>
<span className="mt-0.5 inline-flex h-10 w-10 flex-none items-center justify-center rounded-lg bg-indigo-50 text-indigo-600 ring-1 ring-inset ring-indigo-100 transition-colors group-hover:bg-indigo-100 dark:bg-indigo-500/10 dark:text-indigo-300 dark:ring-indigo-500/20">
<Icon />
</span>
<span className="min-w-0">
<span className="block text-sm font-semibold text-slate-900 dark:text-white">
{item.label}
</span>
<span className="mt-0.5 block text-xs leading-relaxed text-slate-500 dark:text-slate-400">
{item.description}
</span>
</span>
</a>
</li>
);
})}
</ul>
<div className="mt-1 flex items-center justify-between rounded-xl bg-slate-50 px-3 py-2.5 dark:bg-slate-800/60">
<span className="text-xs font-medium text-slate-500 dark:text-slate-400">
New: anomaly forecasting
</span>
<a
href="#tour"
onClick={() => setMenuOpen(false)}
className="inline-flex items-center gap-1 rounded-md text-xs font-semibold text-indigo-600 hover:text-indigo-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-50 dark:text-indigo-300 dark:hover:text-indigo-200 dark:focus-visible:ring-offset-slate-800"
>
Take the tour
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="h-3.5 w-3.5">
<path d="M5 12h14m-6-6 6 6-6 6" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</a>
</div>
</motion.div>
) : null}
</AnimatePresence>
</div>
{leftLinks.map((link) => (
<a key={link.label} href={link.href} className={linkClass}>
{link.label}
</a>
))}
</div>
</div>
{/* Center: logo */}
<a
href="#top"
aria-label="Meridian, home"
className="group flex items-center justify-center gap-2.5 rounded-lg px-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-950"
>
<span className="navx-mark inline-flex">
<svg viewBox="0 0 32 32" fill="none" aria-hidden="true" className="h-8 w-8">
<circle cx="16" cy="16" r="13" className="stroke-slate-900 dark:stroke-white" strokeWidth="1.75" />
<path
d="M16 3c-4 3.4-6 7.9-6 13s2 9.6 6 13c4-3.4 6-7.9 6-13S20 6.4 16 3Z"
className="stroke-indigo-500"
strokeWidth="1.75"
/>
<path d="M3.5 16h25" className="stroke-slate-900/50 dark:stroke-white/50" strokeWidth="1.5" />
</svg>
</span>
<span className="text-lg font-semibold tracking-tight text-slate-900 dark:text-white">
Meridian
</span>
</a>
{/* Right region: split links (desktop) + CTA */}
<div className="flex items-center justify-end gap-1.5">
<div className="hidden items-center gap-1 lg:flex">
{rightLinks.map((link) => (
<a key={link.label} href={link.href} className={`relative ${linkClass}`}>
{link.label}
{link.label === "Changelog" ? (
<span
aria-hidden="true"
className="navx-live-dot absolute right-1 top-1 h-1.5 w-1.5 rounded-full bg-emerald-500"
/>
) : null}
</a>
))}
</div>
<a
href="#start"
className="inline-flex items-center justify-center rounded-lg bg-indigo-600 px-3.5 py-2 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-indigo-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-950"
>
Start free
</a>
</div>
</div>
</nav>
{/* Mobile panel */}
<AnimatePresence>
{mobileOpen ? (
<motion.div
key="mobile-panel"
id={mobileId}
initial={reduce ? { opacity: 0 } : { opacity: 0, height: 0 }}
animate={reduce ? { opacity: 1 } : { opacity: 1, height: "auto" }}
exit={reduce ? { opacity: 0 } : { opacity: 0, height: 0 }}
transition={reduce ? { duration: 0 } : { duration: 0.25, ease: [0.22, 1, 0.36, 1] }}
className="overflow-hidden border-t border-slate-200 bg-white lg:hidden dark:border-slate-800 dark:bg-slate-950"
>
<div className="mx-auto max-w-7xl space-y-6 px-4 py-5 sm:px-6">
<div>
<p className="px-1 pb-2 text-xs font-semibold uppercase tracking-wider text-slate-400 dark:text-slate-500">
Platform
</p>
<ul className="grid gap-1">
{productMenu.map((item) => {
const Icon = item.icon;
return (
<li key={item.label}>
<a
href={item.href}
onClick={() => setMobileOpen(false)}
className="flex items-center gap-3 rounded-xl px-2 py-2.5 transition-colors hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:hover:bg-slate-900 dark:focus-visible:ring-offset-slate-950"
>
<span className="inline-flex h-9 w-9 flex-none items-center justify-center rounded-lg bg-indigo-50 text-indigo-600 dark:bg-indigo-500/10 dark:text-indigo-300">
<Icon />
</span>
<span className="text-sm font-medium text-slate-800 dark:text-slate-100">
{item.label}
</span>
</a>
</li>
);
})}
</ul>
</div>
<div className="grid gap-1 border-t border-slate-100 pt-4 dark:border-slate-900">
{[...leftLinks, ...rightLinks].map((link) => (
<a
key={link.label}
href={link.href}
onClick={() => setMobileOpen(false)}
className="flex items-center justify-between rounded-lg px-2 py-2.5 text-sm font-medium text-slate-700 transition-colors hover:bg-slate-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:text-slate-200 dark:hover:bg-slate-900 dark:focus-visible:ring-offset-slate-950"
>
{link.label}
<svg viewBox="0 0 24 24" fill="none" aria-hidden="true" className="h-4 w-4 text-slate-400">
<path d="m9 6 6 6-6 6" stroke="currentColor" strokeWidth="1.9" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</a>
))}
</div>
<a
href="#start"
onClick={() => setMobileOpen(false)}
className="flex w-full items-center justify-center rounded-lg bg-indigo-600 px-4 py-2.5 text-sm font-semibold text-white transition-colors hover:bg-indigo-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-slate-950"
>
Start free
</a>
</div>
</motion.div>
) : null}
</AnimatePresence>
</header>
{/* Demo content so the sticky + scroll-aware backdrop is observable */}
<div className="mx-auto max-w-7xl px-4 pb-24 pt-16 sm:px-6 lg:px-8 lg:pt-24">
<div className="mx-auto max-w-3xl text-center">
<span className="inline-flex items-center gap-2 rounded-full border border-slate-200 bg-white px-3 py-1 text-xs font-medium text-slate-600 dark:border-slate-800 dark:bg-slate-900 dark:text-slate-300">
<span className="navx-live-dot h-1.5 w-1.5 rounded-full bg-emerald-500" aria-hidden="true" />
Now streaming events in 14ms p95
</span>
<h1 className="mt-6 text-balance text-4xl font-semibold tracking-tight text-slate-900 sm:text-5xl dark:text-white">
Product analytics that keeps up with your release cadence
</h1>
<p className="mx-auto mt-5 max-w-xl text-pretty text-base leading-relaxed text-slate-600 dark:text-slate-400">
Meridian unifies realtime dashboards, cohort retention, and anomaly alerts
over your own warehouse. Scroll to watch the navigation settle into place.
</p>
<div className="mt-8 flex flex-wrap items-center justify-center gap-3">
<a
href="#start"
className="inline-flex items-center justify-center rounded-lg bg-indigo-600 px-5 py-2.5 text-sm font-semibold text-white transition-colors hover:bg-indigo-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-50 dark:focus-visible:ring-offset-slate-950"
>
Start free
</a>
<a
href="#docs"
className="inline-flex items-center justify-center rounded-lg border border-slate-300 bg-white px-5 py-2.5 text-sm font-semibold text-slate-800 transition-colors hover:bg-slate-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-500 focus-visible:ring-offset-2 focus-visible:ring-offset-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100 dark:hover:bg-slate-800 dark:focus-visible:ring-offset-slate-950"
>
Read the docs
</a>
</div>
</div>
<div className="mt-20 grid gap-4 sm:grid-cols-3">
{productMenu.slice(0, 3).map((item) => {
const Icon = item.icon;
return (
<div
key={item.label}
className="rounded-2xl border border-slate-200 bg-white p-6 dark:border-slate-800 dark:bg-slate-900"
>
<span className="inline-flex h-10 w-10 items-center justify-center rounded-lg bg-indigo-50 text-indigo-600 dark:bg-indigo-500/10 dark:text-indigo-300">
<Icon />
</span>
<h2 className="mt-4 text-sm font-semibold text-slate-900 dark:text-white">{item.label}</h2>
<p className="mt-1.5 text-sm leading-relaxed text-slate-500 dark:text-slate-400">
{item.description}
</p>
</div>
);
})}
</div>
<div className="h-[60vh]" aria-hidden="true" />
</div>
</section>
);
}Dependencies
Licence
Built by Web Innoventix. Free for personal and commercial use, no attribution required.
Built by Web Innoventix
Need a custom interface, a full website, or SEO that gets you cited by AI? We design, build and rank it end to end.
Get a free quoteSimilar components
Browse all →
Centred Logo Navbar
OriginalA three-column navbar with the brand logo centred between the primary links and the account actions, wrapped in a soft rounded card.

Left Logo Navbar With CTA
OriginalA classic left-aligned logo navbar with centred navigation links and a prominent pill call-to-action button on the right.

Navbar With Dropdown Menu
OriginalA navbar featuring a JS-free product dropdown built with native details and summary, revealing a two-item mega-menu panel with icons.

Glass Transparent Navbar
OriginalA frosted glass navbar floating over a colourful gradient hero backdrop, using backdrop blur and translucent borders for a modern overlay header.

Responsive Navbar with Mobile Menu
MITA marketing site header with a logo, inline nav links, login/register buttons, and a working hamburger toggle that reveals a stacked mobile menu below the md breakpoint. Fully keyboard/ARIA accessible with light and dark variants.

Underline Tabs
MITAn accessible underline-style tab switcher with a bottom-border active indicator and a live content panel. State-driven with proper role=tablist/tab/tabpanel and aria-selected wiring, styled for light and dark modes.

Split Button Dropdown Menu
MITA split-button control with a chevron trigger that opens a divided action menu, including a destructive delete item. Closes on outside-click and Escape, with a rotating chevron and full ARIA menu roles for both light and dark themes.

Glass Navbar
Originalglassmorphic sticky navbar

Mega Navbar
Originalnavbar with a mega dropdown

Sidebar Toggle Navbar
Originalnavbar with a sidebar toggle

Search Navbar
Originalnavbar with an expanding search

Gradient Navbar
Originalgradient navbar

