Docs
Systhema Design (opens in new tab)
Unreleased

Footer

Add a simple or a multi-column site footer with navigation, social links, copyright, band backgrounds and locale-aware logo links.

On this page

Systhema ships two footers that render from data. FooterSimpleInstance is one centred column: logo, a row of links, social icons and a copyright line. FooterAdvancedInstance has a main band with the logo, text, social links and up to four navigation columns, and a bottom bar with the copyright and legal links. Both render real lists and labelled landmarks, and both are built from exported parts.

FooterAdvancedInstance
import { FooterAdvancedInstance, Paragraph } from '@systhemaui/next'

const logo = (
  <svg viewBox="0 0 150 40" fill="none" role="img" aria-label="Northwind">
    <rect y="4" width="32" height="32" rx="9" fill="currentColor" />
    <path
      d="M9 28V12l14 16V12"
      stroke="var(--color-layout-bg-main)"
      strokeWidth="3.5"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
    <text x="42" y="28" fontSize="19" fontWeight="700" fill="currentColor">
      Northwind
    </text>
  </svg>
)

const instagram =
  '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="5"/><circle cx="12" cy="12" r="4"/><circle cx="17.5" cy="6.5" r="1" fill="currentColor"/></svg>'
const linkedin =
  '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M4 3a2 2 0 1 1 0 4 2 2 0 0 1 0-4zM3 9h3v12H3zM9 9h3v1.7c.6-1 1.8-2 3.7-2C19 8.7 21 10.5 21 14v7h-3v-6.3c0-1.8-.7-3-2.4-3-1.6 0-2.6 1.1-2.6 3V21H9z"/></svg>'

export default function Demo() {
  return (
    <FooterAdvancedInstance
      logo={logo}
      logoHref="#"
      content={<Paragraph>A small studio for brand and web design.</Paragraph>}
      socialItems={[
        { label: 'Instagram', url: 'https://www.instagram.com/', icon: instagram },
        { label: 'LinkedIn', url: 'https://www.linkedin.com/', icon: linkedin },
      ]}
      navigationGroups={[
        {
          type: 'navigation',
          title: 'Studio',
          items: [
            { label: 'Work', url: '#' },
            { label: 'Services', url: '#' },
            { label: 'Journal', url: '#' },
          ],
        },
        {
          type: 'navigation',
          title: 'Company',
          items: [
            { label: 'About', url: '#' },
            { label: 'Careers', url: '#' },
            { label: 'Press kit', url: '#', openInNewTab: true },
          ],
        },
        {
          type: 'content',
          title: 'Visit',
          content: <Paragraph type="small">12 Harbour Street, Northwind. Monday to Friday.</Paragraph>,
        },
      ]}
      copyright="© 2026 Northwind Studio"
      bottomNavigation={[
        { label: 'Privacy', url: '#' },
        { label: 'Terms', url: '#' },
        { label: 'Cookies', url: '#' },
      ]}
    />
  )
}

ImportLink to this section

import { FooterAdvancedInstance, FooterSimpleInstance } from '@systhemaui/next'

In a React app without Next.js, import them from @systhemaui/react.

FooterSimpleInstanceLink to this section

One centred column: the logo and optional content, a row of navigationItems, socialItems and a copyright line. Each part renders only when you pass its prop.

FooterSimpleInstance
import { FooterSimpleInstance } from '@systhemaui/next'

const logo = (
  <svg viewBox="0 0 40 40" fill="none" role="img" aria-label="Northwind">
    <rect y="4" width="32" height="32" rx="9" fill="currentColor" />
    <path
      d="M9 28V12l14 16V12"
      stroke="var(--color-layout-bg-main)"
      strokeWidth="3.5"
      strokeLinecap="round"
      strokeLinejoin="round"
    />
  </svg>
)

const youtube =
  '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M22 8.2a3 3 0 0 0-2.1-2.1C18 5.6 12 5.6 12 5.6s-6 0-7.9.5A3 3 0 0 0 2 8.2 31 31 0 0 0 1.6 12a31 31 0 0 0 .4 3.8 3 3 0 0 0 2.1 2.1c1.9.5 7.9.5 7.9.5s6 0 7.9-.5a3 3 0 0 0 2.1-2.1 31 31 0 0 0 .4-3.8 31 31 0 0 0-.4-3.8zM10 15.2V8.8l5.2 3.2z"/></svg>'
const instagram =
  '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="5"/><circle cx="12" cy="12" r="4"/><circle cx="17.5" cy="6.5" r="1" fill="currentColor"/></svg>'

export default function Demo() {
  return (
    <FooterSimpleInstance
      logo={logo}
      logoHref="#"
      navigationItems={[
        { label: 'Work', url: '#' },
        { label: 'About', url: '#' },
        { label: 'Privacy', url: '#' },
        { label: 'Contact', url: '#' },
      ]}
      socialItems={[
        { label: 'YouTube', url: 'https://www.youtube.com/', icon: youtube },
        { label: 'Instagram', url: 'https://www.instagram.com/', icon: instagram },
      ]}
      copyright="© 2026 Northwind Studio. All rights reserved."
    />
  )
}

FooterAdvancedInstanceLink to this section

The main band holds the logo, content and socialItems on one side and the navigationGroups on the other, as columns that go from one to two (at md) to four (at lg). The bottom bar holds copyright and bottomNavigation.

A navigation group is one of two kinds:

  • { type: 'navigation', title?, items }: a titled list of links ({ label, url, openInNewTab? }).
  • { type: 'content', title?, content }: a titled block of any content, such as an address or opening hours.

Each group's title renders as a real heading (FooterMainNavigationGroupTitle), an <h2> by default. Set elementProps.mainNavigationGroupTitle.as to 'h3' or 'h4' to fit the page's heading outline.

The root <footer> draws a 1px top hairline in --color-foundations-line-muted. The --footer-advanced-border custom property holds the whole border-top value, so a project changes or removes it in its own stylesheet:

@layer app {
  .footer-advanced {
    --footer-advanced-border: none;
  }
}

ExamplesLink to this section

ThemeLink to this section

theme sets data-theme on the <footer>, so the footer, its bands and its links resolve their colors from that color-system mode.

import { FooterSimpleInstance } from '@systhemaui/next'

export default function Demo() {
  return (
    <FooterSimpleInstance
      theme="dark"
      logo={
        <svg viewBox="0 0 150 40" fill="none" role="img" aria-label="Northwind">
          <rect y="4" width="32" height="32" rx="9" fill="currentColor" />
          <text x="42" y="28" fontSize="19" fontWeight="700" fill="currentColor">
            Northwind
          </text>
        </svg>
      }
      logoHref="#"
      navigationItems={[
        { label: 'Imprint', url: '#' },
        { label: 'Privacy', url: '#' },
        { label: 'Status page', url: '#', openInNewTab: true },
      ]}
      copyright="© 2026 Northwind Studio"
    />
  )
}

Tinted bottom bandLink to this section

Each band paints --color-footer-main-background or --color-footer-bottom-background, edge to edge even though the bands are container-width. Set the tokens to change them site-wide (see Band backgrounds); for one footer, override the variable on the band:

Tinted bottom band
import type { CSSProperties } from 'react'
import { FooterAdvancedInstance } from '@systhemaui/next'

export default function Demo() {
  return (
    <FooterAdvancedInstance
      navigationGroups={[
        {
          type: 'navigation',
          title: 'Product',
          items: [
            { label: 'Features', url: '#' },
            { label: 'Pricing', url: '#' },
          ],
        },
        {
          type: 'navigation',
          title: 'Support',
          items: [
            { label: 'Help center', url: '#' },
            { label: 'Contact', url: '#' },
          ],
        },
      ]}
      copyright="© 2026 Northwind Studio"
      bottomNavigation={[
        { label: 'Privacy', url: '#' },
        { label: 'Terms', url: '#' },
      ]}
      elementProps={{
        bottom: {
          style: {
            '--color-footer-bottom-background': 'var(--color-layout-bg-alternative)',
          } as CSSProperties,
        },
      }}
    />
  )
}

Customizing slots with elementPropsLink to this section

elementProps passes props to each part by slot name. FooterSimpleInstance has container, content, navigation, navigationItem, social, socialItem and copyright. FooterAdvancedInstance has main, mainContent, mainNavigation, mainNavigationGroup, mainNavigationGroupTitle, mainNavigationContent, mainNavigationItems, mainNavigationItem, social, socialItem, bottom, copyright and bottomNavigation. A className you pass is added to the part's own classes. In the advanced footer, mainNavigationItem also reaches the links of the bottom navigation.

Social iconsLink to this section

A social item's icon is either an SVG string, rendered inside an Icon.a (this is what Payload's icon picker produces), or a React element, rendered inside a plain link and sized to --footer-social-icon-size. Social links always open in a new tab.

Band backgroundsLink to this section

The footer's surface is its theme plus three band color tokens. There is no layoutBackground prop: a page-background class on the root <footer> would be painted over by the bands.

TokenCSS variableApplies to
footer.main.background--color-footer-main-background.footer-main (advanced footer)
footer.bottom.background--color-footer-bottom-background.footer-bottom (advanced footer)
footer.simple.background--color-footer-simple-background.footer-simple (the simple <footer>)

All three default to the page background, the way the Figma library defines them, so a stock footer shows one uninterrupted surface. They stay a live reference to it, so a theme on the <footer> moves the bands with it. Give a token a color of its own and that band paints on top:

{
  "footer": {
    "bottom": { "background": { "$type": "color", "$value": "{foundations.bg-alternative}" } }
  }
}

.footer-main and .footer-bottom are container-width, so a plain background-color would paint a centred strip. Core paints them full-bleed with a wide box-shadow spread clipped back to the band's own height (clip-path: inset(0 -100vmax)). That needs no extra markup and causes no horizontal overflow; the trade-off is that the two bands paint nothing outside their own block edges.

footer.simple.background lands on the root <footer> itself, at zero specificity (:where(.footer-simple)), so any class you pass through className wins over it.

HeaderInstance, FooterSimpleInstance and FooterAdvancedInstance link their logo to /. On a multi-locale site the site root is locale-specific (/hu under prefix routing, or the locale's own domain), so a bare / sends visitors to the default locale. Pass the locale's home path as logoHref:

import { getSysthemaLocalizedPathname } from '@systhemaui/core/client'

;<FooterSimpleInstance logo={<Logo />} logoHref={getSysthemaLocalizedPathname(locales, locale, '/')} />

Payload projects need none of this: RootHeader and RootFooter resolve logoHref from the request locale, and an explicit logoHref still wins. See Routing helpers for the locale helpers.

PropsLink to this section

FooterSimpleInstanceLink to this section

labels sets the accessible names: footerNavigation (default 'Footer'), socialLinks ('Social media links'), socialLink, the fallback name of a social item without a label ('Social media link'), and opensInNewTab, the new-tab cue ('opens in new tab'). Every other prop goes to the <footer>, which gets id="footer" unless you pass an id.

PropTypeDefaultDescription
themeColorSystem (dark, default)-
logoElement-
logoHrefstring-Where the logo links to. Defaults to /. On a multi-locale site the site root is locale-specific (/hu, or a locale domain's own /), so pass the locale-correct home path here — @systhemaui/payload's RootFooter does that automatically.
contentReactNode-
navigationItemsFooterSimpleNavigationItemProps[]-
socialItemsFooterSimpleSocialItemProps[]-
copyrightstring-
labels{ footerNavigation?, socialLinks?, socialLink?, opensInNewTab? }-
elementProps{ container?, content?, navigation?, navigationItem?, social?, socialItem?, copyright? }-
…and all <footer> attributes
PropTypeDefaultDescription
label (required)string-
url (required)string-
openInNewTabboolean-
PropTypeDefaultDescription
label (required)string-
url (required)string-
icon (required)string | Element-

FooterAdvancedInstanceLink to this section

labels takes socialLinks, socialLink and opensInNewTab, with the same meaning and defaults as on FooterSimpleInstance. The navigation groups' columns are a Columns with an sm column gap and an lg row gap.

PropTypeDefaultDescription
themeColorSystem (dark, default)-
logoElement-
logoHrefstring-Where the logo links to. Defaults to /. On a multi-locale site the site root is locale-specific (/hu, or a locale domain's own /), so pass the locale-correct home path here — @systhemaui/payload's RootFooter does that automatically.
contentReactNode-
socialItemsFooterAdvancedSocialItemProps[]-
navigationGroupsFooterMainNavigationGroupItemProps[]-
copyrightstring-
bottomNavigationFooterMainNavigationItemProps[]-
labels{ socialLinks?, socialLink?, opensInNewTab? }-
elementProps{ main?, mainContent?, mainNavigation?, mainNavigationGroup?, mainNavigationGroupTitle?, mainNavigationContent?, mainNavigationItems?, mainNavigationItem?, social?, socialItem?, bottom?, copyright?, bottomNavigation? }-
…and all <footer> attributes
PropTypeDefaultDescription
label (required)string-
url (required)string-
icon (required)string | Element-
PropTypeDefaultDescription
type (required)'content' | 'navigation'-
titlestring-
contentReactNode-
itemsFooterMainNavigationItemProps[]-
PropTypeDefaultDescription
label (required)string-
url (required)string-
openInNewTabboolean-
PropTypeDefaultDescription
label (required)string-
url (required)string-
openInNewTabboolean-

PartsLink to this section

Simple footer: FooterSimpleContainer, FooterSimpleContent, FooterSimpleNavigation, FooterSimpleNavigationItem, FooterSimpleSocial, FooterSimpleSocialItem, FooterSimpleCopyright.

Advanced footer: FooterMain, FooterMainContent, FooterAdvancedSocial, FooterAdvancedSocialItem, FooterMainNavigation, FooterMainNavigationGroup, FooterMainNavigationGroupTitle, FooterMainNavigationContent, FooterMainNavigationItems, FooterNavigationItem, FooterBottom, FooterBottomCopyright, FooterBottomNavigation.

Each part renders the element and classes shown below and passes other props through.

HTML and CSSLink to this section

The two footers in plain HTML:

<footer class="footer footer-simple" id="footer">
  <div class="footer-container footer-simple-container container">
    <div class="footer-content footer-simple-content">
      <a class="footer-logo" href="/">…</a>
    </div>
    <nav class="footer-navigation footer-simple-navigation" aria-label="Footer">
      <ul class="footer-list">
        <li><a class="footer-navigation-item footer-simple-navigation-item" href="/work">Work</a></li>
      </ul>
    </nav>
    <nav class="footer-social footer-simple-social" aria-label="Social media links">
      <ul class="footer-list">
        <li>
          <a
            class="icon footer-simple-social-item footer-social-item"
            href="https://www.instagram.com/"
            target="_blank"
            rel="noopener noreferrer"
            aria-label="Instagram (opens in new tab)"
            >…</a
          >
        </li>
      </ul>
    </nav>
    <div class="footer-simple-copyright footer-copyright">© 2026 Northwind Studio</div>
  </div>
</footer>

<footer class="footer-advanced text-body footer" id="footer">
  <div class="footer-main container">
    <div class="footer-content footer-main-content">…</div>
    <div class="columns footer-main-navigation columns-gap-x-sm columns-gap-y-lg">
      <div class="column richtext footer-main-navigation-group">
        <h2 class="text-body footer-main-navigation-group-title font-bold">Studio</h2>
        <div class="footer-navigation footer-main-navigation-group-items">
          <ul class="footer-list">
            <li><a class="footer-navigation-item" href="/work">Work</a></li>
          </ul>
        </div>
      </div>
    </div>
  </div>
  <div class="gap-sm footer-bottom container">
    <div class="text-small footer-bottom-copyright">© 2026 Northwind Studio</div>
    <div class="text-small footer-navigation footer-bottom-navigation">…</div>
  </div>
</footer>
ClassStyles
footerdisplay: flex;flex-direction: column;
footer-logo& { display: inline-block; width: auto; height: var(--footer-logo-height, 48px); color: var(--color-footer-logo, inherit); }& > * { display: inline-block; width: auto; height: var(--footer-logo-height, 48px); color: var(--color-footer-logo, inherit); }
footer-content& { display: flex; flex-direction: column; row-gap: var(--typography-block-margin-y, 16px); }:is(.footer-content), .footer-content > [class*="color-"] { color: var(--color-footer-text, inherit); }
footer-navigation& { display: flex; flex-wrap: wrap; }& a { color: var(--color-footer-link, inherit); }.footer-navigation a { &:hover { color: var(--color-footer-link-hover, inherit); } }
footer-socialdisplay: flex;flex-direction: row;flex-wrap: wrap;align-items: center;gap: var(--footer-social-gap, 24px);
footer-social-item& { color: var(--color-footer-icon, inherit); }&:is(a):hover { color: var(--color-footer-icon-hover, inherit); }& > svg, .footer-social-item > img { display: inline-block; width: var(--footer-social-icon-size, var(--icon-size)); height: var(--footer-social-icon-size, var(--icon-size)); object-fit: contain; object-position: center; }
footer-list& { list-style: none; margin: 0; padding: 0; display: contents; }& > li { display: contents; }
footer-copyrightcolor: var(--color-footer-text, inherit);
footer-simple:where(.footer-simple) { background-color: var(--color-footer-simple-background, inherit); }
footer-simple-containerdisplay: flex;flex-direction: column;align-items: center;padding-block: var(--footer-simple-padding-y, 48px);row-gap: var(--footer-simple-gap-y, 32px);
footer-simple-contentalign-items: center;text-align: center;max-width: var(--footer-simple-content-max-width, 100%);
footer-simple-navigationflex-direction: row;justify-content: center;gap: var(--footer-simple-navigation-gap, 12px);
footer-simple-socialjustify-content: center;
footer-simple-copyrighttext-align: center;
footer-advancedborder-top: var(--footer-advanced-border, 1px solid var(--color-foundations-line-muted));
footer-maindisplay: flex;flex-direction: column;justify-content: flex-start;padding-block: var(--footer-main-padding-y, 48px);row-gap: var(--footer-main-gap-y, 48px);background-color: var(--color-footer-main-background);box-shadow: 0 0 0 100vmax var(--color-footer-main-background);clip-path: inset(0 -100vmax);
footer-main-contentalign-items: flex-start;justify-content: flex-start;
footer-main-navigation.columns.footer-main-navigation { --columns-cols: 1; }@media (width >= 640px) { .columns.footer-main-navigation { --columns-cols: 2; } }@media (width >= 1192px) { .columns.footer-main-navigation { --columns-cols: 4; } }
footer-main-navigation-groupdisplay: flex;flex-direction: column;align-items: flex-start;justify-content: flex-start;
footer-main-navigation-group-itemsdisplay: flex;flex-direction: column;align-items: flex-start;justify-content: flex-start;row-gap: var(--footer-main-navigation-items-gap-y, 12px);
footer-main-navigation-group-titlemargin-bottom: var(--footer-main-navigation-title-margin-bottom, 12px);color: var(--color-footer-title, inherit);
footer-bottom& { display: flex; flex-direction: column-reverse; align-items: flex-start; justify-content: flex-start; padding-block: var(--footer-bottom-padding-y, 24px); background-color: var(--color-footer-bottom-background); box-shadow: 0 0 0 100vmax var(--color-footer-bottom-background); clip-path: inset(0 -100vmax); }@media (width >= 640px) { & { flex-direction: row; align-items: center; justify-content: space-between; } }
footer-bottom-copyrightdisplay: flex;flex-direction: row;flex-wrap: wrap;align-items: center;justify-content: flex-start;gap: var(--footer-bottom-copyright-gap, 8px);color: var(--color-footer-text, inherit);
footer-bottom-navigationdisplay: flex;flex-direction: row;flex-wrap: wrap;align-items: center;justify-content: flex-end;gap: var(--footer-bottom-navigation-gap, 16px);

Next.jsLink to this section

The Next footers render the logo and every navigation link through the Next link helper, so internal links navigate client-side and are prefetched. Social items given as SVG strings use the Next Icon.a.

AccessibilityLink to this section

  • Navigation and social links are real <ul>/<li> lists, announced with their item count. The list elements are display: contents, so the layout is unaffected.
  • Each link collection is a <nav> landmark with its own label (labels.footerNavigation, labels.socialLinks). The advanced footer's link columns sit under their group headings instead.
  • Links that open in a new tab (any openInNewTab item, and every social link) say so to assistive technology: navigation items with visually hidden "(opens in new tab)" text, social items in their aria-label. They carry rel="noopener noreferrer".
  • The group titles are real headings, <h2> by default; adjust them with elementProps.mainNavigationGroupTitle.as so the outline has no gaps.
  • The logo link takes its accessible name from the logo. Give an SVG logo role="img" and an aria-label (as in the previews), an image an alt, or add visually hidden text (<span className="sr-only!">Northwind</span>) as the Payload template does.