Bitcart UI SDK Documentation

Layout configuration

Config format

The UI Kit integration starts with the layout config: a declarative object that defines the brand and navigation settings and encapsulates the basic internationalization layer state.

Lazy evaluation

The UI Kit uses Lingui for internationalization, and the layout config is expected to have translatable strings. This makes the active locale ID, along with every user-facing string, valid only for the locale active when the config is built. Building the config as a static object would capture that state only once, leaving the key parts of the layout unresponsive to locale changes.

The config is therefore built by a factory that takes the i18n instance from useLingui() and translates every string through it with i18n.t(msg`…`). That instance changes on every locale switch and catalog load, so calling the factory with it, memoized on it, yields a config resolved for the current locale. Avoid the global i18n and the t macro from @lingui/core/macro here: they are hidden inputs, and React Compiler caches a call on its arguments, so a factory reading them would keep the first locale forever.

Example

// src/pages/layout.config.ts

import { defineGetLayoutConfig } from "@bitcart/ui-kit/utils"
import type { I18n } from "@lingui/core"
import { msg } from "@lingui/core/macro"

import { GenericCompanyLogoIcon, RepositoryIcon } from "./icons"

const AVAILABLE_LOCALES = ["en", "de", "fr"]

// 💡 `defineGetLayoutConfig` enforces the config schema at the definition site
//    and keeps the factory's parameters.
export const getLayoutConfig = defineGetLayoutConfig((i18n: I18n) => ({
  i18n: {
    activeLocale: i18n.locale,
    availableLocales: AVAILABLE_LOCALES,
  },

  brand: {
    name: "Generic Company",
    tagline: i18n.t(msg`Everything a small business needs`),
    logoIcon: GenericCompanyLogoIcon,
    logoImageSrc: "/logo.svg",
  },

  project: {
    canonicalName: "Storefriend",
    copyrightSinceYear: 2019,
    description: i18n.t(msg`User-friendly storefront constructor`),
  },

  navigation: {
    navBarDisplayCapacity: { md: 2, lg: 4, xl: 5, "2xl": 6, "3xl": 6 },

    directory: {
      labeledLinks: [
        {
          groupTitle: i18n.t(msg`Product`),

          items: [
            { label: i18n.t(msg`Features`), href: "/#features", globalPriority: 1 },
            { label: i18n.t(msg`Pricing`), href: "/pricing", globalPriority: 2 },
          ],
        },

        {
          groupTitle: i18n.t(msg`Resources`),

          items: [
            {
              label: i18n.t(msg`Documentation`),
              shortLabel: i18n.t(msg`Docs`),
              href: "https://docs.example.com",
              isExternal: true,
              globalPriority: 3,
            },
          ],
        },
      ],

      iconLinks: [
        {
          groupTitle: i18n.t(msg`Project links`),
          footerOnly: true,

          items: [
            {
              icon: RepositoryIcon,
              hint: i18n.t(msg`Browse the source code`),
              href: "https://example.com/source",
              isExternal: true,
            },
          ],
        },
      ],
    },
  },
}))

Layout context

The layout context is the integration seam between a host application and the UI Kit's application state- and metadata-aware components. It carries everything those components need but only the application can know, and distributes that knowledge to the entire layout tree, forming the backbone of an application shell. Components that strictly depend on it are marked accordingly in their documentation.

Provider setup

LayoutContextProvider is the single wiring point: one provider, configured once with plain data, makes the entire component tree routable, branded, and localized.

It must be mounted once, at the root level of the application layout, above everything that renders UI Kit components:

import { Link, useClientRoute } from "@bitcart/vike-kit/navigation"
import { LayoutContextProvider } from "@bitcart/ui-kit/providers"
import { useLingui } from "@lingui/react"
import { useMemo } from "react"
import { useHydrated } from "vike-react/useHydrated"

import { getLayoutConfig } from "./layout.config"

export default function Layout({ children }: { children: React.ReactNode }) {
  const { i18n } = useLingui()
  const route = useClientRoute()
  const hydrated = useHydrated()

  // 💡 Every new config object rerenders the layout: build it once per locale.
  const layoutConfig = useMemo(() => getLayoutConfig(i18n), [i18n])

  return (
    // 💡 Provider's props have built-in documentation, which should be available
    //    to every IDE with a properly functioning TypeScript language service.
    <LayoutContextProvider
      LinkComponent={Link}
      currentRoute={route}
      isHydrated={hydrated}
      layoutConfig={layoutConfig}
    >
      {children}
    </LayoutContextProvider>
  )
}

The example above is purely illustrative and the application-side details will differ depending on the framework used.

Consumer access

The existing layout context consumers from the UI Kit already use useLayoutContext() internally to access the context value. However, should the need arise, the hook can be used at the application level within the provider's scope:

import { useLayoutContext } from "@bitcart/ui-kit/hooks"

export const ExampleComponent = () => {
  const {
    // 💡 The built-in documentation is available here too!
    layoutConfig: { brand },
  } = useLayoutContext()

  return (
    <div className="flex flex-col justify-center items-center gap-2">
      <h1 className="flex items-center gap-2">
        <brand.logoIcon className="size-5" />
        <span>{brand.name}</span>
      </h1>

      <h2>{brand.tagline}</h2>
    </div>
  )
}

On this page