Skip to content
← Blocks

Marketing Footer

A complete marketing footer 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-footer.json

Use it

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

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

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 {
  MarketingFooter,
  MarketingFooterBrand,
  MarketingFooterLinks,
  MarketingFooterNote,
} from "@/components/jez-ui/blocks/marketing-footer";

export default function Example() {
  return (
    <MarketingFooter>
      <MarketingFooterBrand />
      <MarketingFooterLinks />
      <MarketingFooterNote />
    </MarketingFooter>
  );
}
Scroll horizontally for long lines
import { MarketingFooter, MarketingFooterNavigation, MarketingFooterBrand, MarketingFooterLinks, MarketingFooterNote } from "@/components/jez-ui/blocks/marketing-footer";
Read the composition guide →

Props & types

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

export type MarketingFooterProps = Omit<
  React.ComponentProps<"footer">,
  keyof MarketingFooterOptions
> &
  MarketingFooterOptions;

MarketingFooterProps

Source

View 2 source files

registry/blocks/marketing-footer.tsx

Scroll horizontally for long lines
"use client";
import * as React from "react";
import { cn } from "../ui/utils";
export type MarketingFooterOptions = {
  brand?: string;
  items?: { label: string; href: string }[];
  className?: string;
};
export type MarketingFooterProps = Omit<
  React.ComponentProps<"footer">,
  keyof MarketingFooterOptions
> &
  MarketingFooterOptions;
function useMarketingFooterModel({
  brand = "Forma",
  items = [
    { label: "Contact", href: "#contact" },
    { label: "About", href: "#about" },
  ],
  className,
  children,
  ...rootProps
}: MarketingFooterProps) {
  return { brand, items, className, children, rootProps };
}
const MarketingFooterCompositionContext = React.createContext<ReturnType<
  typeof useMarketingFooterModel
> | null>(null);
function useMarketingFooterComposition() {
  const context = React.useContext(MarketingFooterCompositionContext);
  if (!context)
    throw new Error("MarketingFooter parts must be inside MarketingFooter.");
  return context;
}
export function MarketingFooter(props: MarketingFooterProps) {
  const model = useMarketingFooterModel(props);
  const { className, rootProps, children } = model;
  return (
    <MarketingFooterCompositionContext.Provider value={model}>
      <footer
        {...rootProps}
        className={cn(
          "mt-16 flex flex-wrap items-center justify-between gap-6 border-t border-border py-8 text-sm",
          className,
        )}
      >
        {children !== undefined ? (
          children
        ) : (
          <>
            <MarketingFooterBrand />
            <MarketingFooterLinks />
            <MarketingFooterNote />
          </>
        )}
      </footer>
    </MarketingFooterCompositionContext.Provider>
  );
}

export function MarketingFooterNavigation({
  className,
  ...props
}: React.ComponentProps<"nav">) {
  return (
    <nav
      data-slot="marketing-footer-navigation"
      className={cn("flex gap-5", className)}
      {...props}
    />
  );
}

export function MarketingFooterBrand({
  children,
  ...props
}: Partial<React.ComponentProps<"span">> & { children?: React.ReactNode }) {
  const { brand } = useMarketingFooterComposition();
  return (
    <span
      {...props}
      className={cn("font-display text-xl font-medium", props.className)}
    >
      {children === undefined ? brand : children}
    </span>
  );
}
export function MarketingFooterLinks({
  children,
  ...props
}: Partial<React.ComponentProps<typeof MarketingFooterNavigation>> & {
  children?: React.ReactNode;
}) {
  const { items } = useMarketingFooterComposition();
  return (
    <MarketingFooterNavigation aria-label="Footer" {...props}>
      {children === undefined
        ? items.map((i) => (
            <a key={i.href} href={i.href}>
              {i.label}
            </a>
          ))
        : children}
    </MarketingFooterNavigation>
  );
}
export function MarketingFooterNote({
  children,
  ...props
}: Partial<React.ComponentProps<"span">> & { children?: React.ReactNode }) {
  return (
    <span
      {...props}
      className={cn("text-xs text-muted-foreground", props.className)}
    >
      {children === undefined ? "A Jez UI demo template." : children}
    </span>
  );
}

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));
}

Dependencies

clsx@2.1.1 · tailwind-merge@3.5.0

Accessibility & behaviour

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

Read the accessibility and performance guide →