Skip to content
← Blocks

Marketing Navigation

A complete marketing navigation section, ready to adapt to your product.

Open ↗

Make it yours.

Install the editable source and its dependencies into your React + Tailwind project.

Scroll horizontally for long lines
pnpm dlx shadcn@4.0.8 add http://localhost:3000/r/marketing-navigation.json

Use it

Scroll horizontally for long lines
"use client";
import { MarketingNavigation } from "@/components/jez-ui/blocks/marketing-navigation";

export default function Example() {
  return (
    <>
      <MarketingNavigation />
    </>
  );
}

Compose your own

These styled parts are included in the same install. Use children to build your layout; add className only when you want an override.

Scroll horizontally for long lines
"use client";
import {
  MarketingNavigation,
  MarketingNavigationBrand,
  MarketingNavigationLinks,
} from "@/components/jez-ui/blocks/marketing-navigation";
import {
  NavigationMenuList,
  NavigationMenuItem,
  NavigationMenuLink,
} from "@/components/jez-ui/ui/navigation-menu";
export default function Example() {
  return (
    <MarketingNavigation>
      <MarketingNavigationBrand asChild>
        <a href="#home">Our studio</a>
      </MarketingNavigationBrand>
      <MarketingNavigationLinks>
        <NavigationMenuList>
          <NavigationMenuItem>
            <NavigationMenuLink asChild>
              <a href="#work">Our work</a>
            </NavigationMenuLink>
          </NavigationMenuItem>
          <NavigationMenuItem>
            <NavigationMenuLink asChild>
              <a href="#contact">Contact us</a>
            </NavigationMenuLink>
          </NavigationMenuItem>
        </NavigationMenuList>
      </MarketingNavigationLinks>
    </MarketingNavigation>
  );
}
Scroll horizontally for long lines
import { MarketingNavigation, MarketingNavigationBrand, MarketingNavigationLinks } from "@/components/jez-ui/blocks/marketing-navigation";
Read the composition guide →

Props & types

Scroll horizontally for long lines
export type MarketingNavigationOptions = {
  brand?: string;
  items?: { label: string; href: string }[];
  home?: string;
  className?: string;
};

export type MarketingNavigationProps = Omit<
  React.ComponentProps<"header">,
  keyof MarketingNavigationOptions
> &
  MarketingNavigationOptions;

MarketingNavigationProps

Source

View 3 source files

registry/blocks/marketing-navigation.tsx

Scroll horizontally for long lines
"use client";
import * as React from "react";
import { Slot } from "radix-ui";
import { cn } from "../ui/utils";
import { NavigationMenu } from "../ui/navigation-menu";
export type MarketingNavigationOptions = {
  brand?: string;
  items?: { label: string; href: string }[];
  home?: string;
  className?: string;
};
export type MarketingNavigationProps = Omit<
  React.ComponentProps<"header">,
  keyof MarketingNavigationOptions
> &
  MarketingNavigationOptions;
function useMarketingNavigationModel({
  brand = "Forma",
  items = [
    { label: "Features", href: "#features" },
    { label: "Pricing", href: "#pricing" },
    { label: "Contact", href: "#contact" },
  ],
  home = "/",
  className,
  children,
  ...rootProps
}: MarketingNavigationProps) {
  return { brand, items, home, className, children, rootProps };
}
const MarketingNavigationCompositionContext = React.createContext<ReturnType<
  typeof useMarketingNavigationModel
> | null>(null);
function useMarketingNavigationComposition() {
  const context = React.useContext(MarketingNavigationCompositionContext);
  if (!context)
    throw new Error(
      "MarketingNavigation parts must be inside MarketingNavigation.",
    );
  return context;
}
export function MarketingNavigation(props: MarketingNavigationProps) {
  const model = useMarketingNavigationModel(props);
  const { className, rootProps, children } = model;
  return (
    <MarketingNavigationCompositionContext.Provider value={model}>
      <header
        {...rootProps}
        className={cn(
          "flex flex-wrap items-center justify-between gap-4 border-b border-border py-5",
          className,
        )}
      >
        {children !== undefined ? (
          children
        ) : (
          <>
            <MarketingNavigationBrand />
            <MarketingNavigationLinks />
          </>
        )}
      </header>
    </MarketingNavigationCompositionContext.Provider>
  );
}

export function MarketingNavigationBrand({
  children,
  asChild,
  ...props
}: Partial<React.ComponentProps<"a">> & { children?: React.ReactNode } & {
  asChild?: boolean;
}) {
  const Comp = asChild ? Slot.Root : "a";
  const { brand, home } = useMarketingNavigationComposition();
  return (
    <Comp
      href={home}
      {...props}
      className={cn(
        "font-display text-2xl font-bold no-underline",
        props.className,
      )}
    >
      {children === undefined ? brand : children}
    </Comp>
  );
}
export function MarketingNavigationLinks({
  children,
  ...props
}: Partial<React.ComponentProps<typeof NavigationMenu>> & {
  children?: React.ReactNode;
}) {
  const { items } = useMarketingNavigationComposition();
  return (
    <NavigationMenu
      {...props}
      items={children === undefined ? (props.items ?? items) : undefined}
    >
      {children}
    </NavigationMenu>
  );
}

registry/ui/utils.ts

Scroll horizontally for long lines
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
export function cn(...values: ClassValue[]) {
  return twMerge(clsx(values));
}

registry/ui/navigation-menu.tsx

Scroll horizontally for long lines
"use client";
import * as React from "react";
import { NavigationMenu as P } from "radix-ui";
import { ChevronDown } from "lucide-react";
import { cn } from "./utils";
export function NavigationMenu({
  items,
  children,
  className,
  ...props
}: React.ComponentProps<typeof P.Root> & {
  items?: { label: React.ReactNode; href: string }[];
}) {
  return (
    <P.Root className={cn("relative", className)} {...props}>
      {items ? (
        <NavigationMenuList>
          {items.map((i) => (
            <NavigationMenuItem key={i.href}>
              <NavigationMenuLink href={i.href}>{i.label}</NavigationMenuLink>
            </NavigationMenuItem>
          ))}
        </NavigationMenuList>
      ) : (
        children
      )}
    </P.Root>
  );
}
export function NavigationMenuList({
  className,
  ...props
}: React.ComponentProps<typeof P.List>) {
  return (
    <P.List className={cn("flex flex-wrap gap-1", className)} {...props} />
  );
}
export const NavigationMenuItem = P.Item;
export function NavigationMenuLink({
  className,
  ...props
}: React.ComponentProps<typeof P.Link>) {
  return (
    <P.Link
      className={cn(
        "block rounded-lg px-3 py-2 text-sm outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-primary data-[active]:bg-muted",
        className,
      )}
      {...props}
    />
  );
}
export function NavigationMenuTrigger({
  className,
  children,
  ...props
}: React.ComponentProps<typeof P.Trigger>) {
  return (
    <P.Trigger
      className={cn(
        "group flex items-center gap-2 rounded-lg px-3 py-2 text-sm outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-primary",
        className,
      )}
      {...props}
    >
      {children}
      <ChevronDown
        size={14}
        className="transition-transform group-data-[state=open]:rotate-180"
      />
    </P.Trigger>
  );
}
export function NavigationMenuContent({
  className,
  ...props
}: React.ComponentProps<typeof P.Content>) {
  return (
    <P.Content
      className={cn(
        "jez-popover absolute left-0 top-full z-30 grid w-72 max-w-[calc(100vw-32px)] gap-1 rounded-xl border border-border bg-background p-3 text-foreground shadow-xl",
        className,
      )}
      {...props}
    />
  );
}

Dependencies

radix-ui@1.6.7 · clsx@2.1.1 · tailwind-merge@3.5.0 · lucide-react@0.577.0

Accessibility & behaviour

Provide meaningful labels and preserve keyboard focus styles. Check contrast when customising theme colours.

Read the accessibility and performance guide →