Broadleaf Microservices
  • v1.0.0-latest-prod

Unified Admin Release Notes for 2.1.0

Important
  • Requires Node ≥22, React ≥19, React Router ≥6

Features & Enhancements

Introduced ThemeProvider and update existing CSS

Tip
This provides Runtime Admin Theming Support.

We introduced a robust, CSS-variable-based runtime theming engine for the Admin application. The foundation is highly extensible, memory-leak safe, and 100% backwards compatible for downstream apps that do not wish to utilize theming.

Key Features

  • Dynamic Theming Engine: Introduced a new <ThemeProvider> that programmatically accepts a nested Theme object and dynamically injects scoped CSS variables (e.g., --tw-colors-primary-500) into the document :root.

  • AdminProvider Integration: Integrated the new <ThemeProvider> directly into <AdminProvider>, allowing the theme to be passed cleanly as a top-level prop without disturbing existing context setups.

  • Tailwind Plugin & Config Upgrades:

    • Updated @broadleaf/admin-tailwindcss to utilize safe CSS variable fallbacks across all branding colors, mapping gray to the secondary scale and red to the danger scale.

    • Added a new adminTheme Tailwind plugin to dynamically generate shell-specific styling utilities (tw-shell-nav- and tw-shell-header-).

  • Shell Chrome Migration: Surgically migrated legacy SCSS from structural components like <Navigation> and <Header> to leverage the new theme-aware Tailwind classes instead of hardcoded background colors.

  • SASS Color Function Removal: Removed legacy SASS functions (like lighten() or color-yiq()) from older components in @broadleaf/admin-style, allowing them to safely compile and consume the new dynamic CSS variable fallbacks seamlessly.

How it works

You can now theme the admin by supplying a theme prop to your <AdminProvider>:

import { AdminProvider, Theme } from '@broadleaf/admin-components';

const brandTheme: Theme = {
  colors: {
    primary: { /* 50-950 scale */ },
    secondary: { /* 50-950 scale (defaults to Tailwind 'gray') */ },
    danger: { /* 50-950 scale (defaults to Tailwind 'red') */ },
    link: '#3b82f6',
    linkHover: '#2563eb'
  },
  shell: {
    nav: { bg: '#111827', text: '#d1d5db', active: '#1f2937' },
    header: { bg: '#1f2937' }
  }
};

const App = () => (
  <AdminProvider theme={brandTheme}>
    {/* Admin Routes */}
  </AdminProvider>
);

Cleaned up BaseLayout to be only concerned with layout

We introduced a significant architectural cleanup to the <BaseLayout> component and its associated global elements. The goal of this refactor is to improve component cohesion, enable granular overrides for downstream consumers, and future-proof the codebase for modern React paradigms like Server Components (RSC).

1. Global Context Provider Extraction

The <BaseLayout> component was previously cluttered with global context providers and structural modals. These have been cleanly extracted: * ToastContainerContextProvider: Extracted the toast state logic into its own dedicated context provider. * ConfirmModalContextProvider: Extracted the confirmation modal state. The actual <ConfirmModal /> element is now rendered natively inside this provider (after {children}) rather than hanging off the <BaseLayout>. * Both of these new providers have been injected into the default array of the <GlobalProviders> component, ensuring they are available across all routes without cluttering the layout tree.

2. Component Overrides

To provide maximum flexibility for downstream applications, the following global UI components have been wrapped in enableOverrides, allowing them to be swapped out completely: * ConfirmModal * ToastContainer * TransitionToasts * Toast

3. Flat Exports for <ScrollView> (Tree-shaking & RSC Support)

The <ScrollView> component was previously exposing its sub-components via dot-notation (e.g., ScrollView.Header). This has been refactored to use named flat exports (ScrollViewHeader, ScrollViewContent, ScrollViewFooter). * Why? Flat exports drastically improve tree-shaking by allowing bundlers to eliminate dead code. Furthermore, in React Server Component (RSC) environments (like Next.js App Router), dot-notation forces the entire object tree to be treated as a Client Component. Flat exports allow for granular server/client boundary definitions. * Backwards Compatibility: The dot-notation accessors have been kept intact on the default export but are marked as @deprecated.

4. Header & AuthService Cohesion

Removed AuthService dependency injection from <BaseLayout>. The <Header> component now directly imports and handles its own logout and changePassword logic, resulting in a much dumber, cleaner layout wrapper.

Deprecations

The following properties/accessors have been marked as @deprecated and should be migrated to their new paradigms: * ScrollView.Header → Use named import ScrollViewHeader * ScrollView.Content → Use named import ScrollViewContent * ScrollView.Footer → Use named import ScrollViewFooter * HeaderProps.onLogoutClick → Handled internally by <Header> * HeaderProps.onChangePasswordClick → Handled internally by <Header>

Enabled decoupling the rendering of Admin NavMenuItems from Routes

Important
Requires Admin Navigation Services 3.0.0, which is part of Release Train 3.0.0 but also compatible with Release Train 2.3.1 if upgrading independently.

We introduced the ability for Menu Items defined in the Admin Navigation Service to be rendered independently of any Metadata Routes. They can now be filtered by their own predicates in a similar fashion to Routes, e.g., only return this item in a Tenant context rather than an Application. They can also define the security scopes for whether the user has access to see them similar to Routes, e.g., a user needs READ_PRODUCT to see this item.

  • Introduced VITE_MAIN_NAVIGATION_BEHAVIOR. To trigger the new behavior set it to ROUTE_INDEPENDENT

  • Introduced new hook, useSecureNavMenuItem to check whether the user has access to a NavMenuItem either using the old behavior based on a matching route or the new behavior checking the scopes of the NavMenuItem directly

  • Added debug logging to help diagnose why menu items appear or don’t appear

    • Enable using VITE_

  • Fixed lack of typing and documentation for various pieces of the UserOperationContext and related reducers to address technical debt in this area.

  • Deprecated useGetMatchingRoutesWithAccess and useHasMatchingRoute. Instead, useSecureNavMenuItem should be used as it returns both whether the module or submodule has access as well as the map of URL/path for each submodule to whether it has access specifically.

  • Removed access check in NavSection as the access checks for each section has already been done by the time the component is rendered. Instead filtration of the submenu items is explicitly done before rendering any NavSections.

Modularized Metadata Route Rendering & Shell Extraction

We decoupled the application routing logic from the structural UI shell. Previously, AdminApp was tightly coupled to both the layout and the React Router definition, making it difficult for downstream consumers to inject custom routing or entirely replace the application shell.

1. Extraction of AdminRoutes

The react-router-dom <Routes> definition and the metadata-driven route grouping logic have been extracted from AdminApp into a new, standalone <AdminRoutes> component. * This component is purely responsible for consuming the ComponentRouterContext and dynamically rendering the ComponentRouteResolver for each path. * New Injection Slots: <AdminRoutes> now accepts beforeRoutes and afterRoutes props (typed as ReactNode). This allows downstream consumers to seamlessly inject custom static routes alongside the dynamically generated metadata routes without interfering with the 404 catch-all logic.

2. Shell Injection via AdminApp (Slot Pattern)

The <AdminApp> component has been refactored to fully support the slotted children pattern. * If children are provided to <AdminApp>, it now acts strictly as a lifecycle/routing wrapper and renders the provided children directly. This explicitly supports downstream architectures (such as Capibara) that need to inject and manage their own UI shell layout. * Backwards Compatibility: If no children are provided, <AdminApp> gracefully falls back to the original Out-Of-The-Box (OOB) behavior, rendering <BaseLayout><AdminRoutes /></BaseLayout>.

3. Routing Context Lifecycle Audit

Verified that the ComponentRouterContext population is strictly decoupled from the UI shell rendering. The ComponentRouter gateway successfully blocks rendering via the GatewayProvider until all metadata routes are fetched and the context is fully hydrated. This guarantees that any custom shell or route definition passed into the application will have immediate, synchronous access to the fully resolved routing state.

Optimized fulfillment inventory data loading

  • Optimize fulfillment inventory loading to reduce redundant requests due to component re-renders

  • Fixed an issue where an incorrect quantity value was used during fulfillment

  • Updated useFulfillmentInventory to replace the skuCodes parameter with fulfillments

  • Separated inventoryQuantities from StatusChangeContext into a new InventoryContext

    • Note: A withInventoryContext provider is available to allow existing components taking in an inventoryQuantities prop to continue working as previously

Enhanced Extensibility of the Application Selector Component

Moved internal logic of <ApplicationSelector> into a useApplicationSelector hook, encapsulating the state, fetch orchestration, authorization logic, and event callbacks for selecting the active application context. This allows easier customization of the <ApplicationSelector> in the nav sidebar.

import useApplicationSelector from '@broadleaf/admin-components/dist/view/components/ApplicationSelector/hooks/useApplicationSelector';

export const CustomApplicationSelector = () => {
  const {
    application,
    applications,
    isLoading,
    formattedMessages,
    handleApplicationChange,
    queueFetchApplications,
    resolveApplicationsWithQuery
  } = useApplicationSelector();

  return <>{/* Custom Selector Component */}</>;
}

Added support for overriding out-of-box Application and Metadata splash screens

These components are rendered while resolving the Application and Metadata Routes or when those requests encounter an error. This covers the following:

  • App-Level Error and Loading Splash (AppSplash)

    • Registered as blAppSplash

    • Subcomponents that support overrides

      • blAppSplashOverlay for AppSplashOverlay

      • blAppSplashBackground for AppSplashBackground

      • blAppSplashLoading for AppSplashLoading

      • blAppSplashError for AppSplashError

      • blAppSplashErrorNotFound for AppSplashErrorNotFound

      • blAppSplashErrorUnauthorized for AppSplashErrorUnauthorized

      • blAppSplashErrorServer for AppSplashErrorServer

      • blAppSplashErrorUnknown for AppSplashErrorUnknown

      • blAppSplashErrorTemplate for AppSplashErrorTemplate

  • Route/Metadata-Level Error and Loading Splash (ComponentRouteSplash)

    • Registered as blComponentRouteSplash

      • Subcomponents that support overrides

  • blTransitionComponentRouteSplashOverlay for TransitionComponentRouteSplashOverlay

  • blComponentRouteSplashOverlay for ComponentRouteSplashOverlay

  • blComponentRouteSplashBackground for ComponentRouteSplashBackground

  • blComponentRouteSplashLoading for ComponentRouteSplashLoading

  • blComponentRouteSplashError for ComponentRouteSplashError

  • blComponentRouteSplashErrorNotFound for ComponentRouteSplashErrorNotFound

  • blComponentRouteSplashErrorUnauthorized for ComponentRouteSplashErrorUnauthorized

  • blComponentRouteSplashErrorServer for ComponentRouteSplashErrorServer

  • blComponentRouteSplashErrorUnknown for ComponentRouteSplashErrorUnknown

  • blComponentRouteSplashErrorTemplate for ComponentRouteSplashErrorTemplate

Modularized the Main Navigation Components

We refactored the monolithic <Navigation> component into a set of highly granular, composable layout building blocks backed by a centralized context provider. These changes enable users to build entirely custom navigation shells, e.g., top-level headers, custom mobile sidebars, off-canvas menus, while preserving the exact out-of-the-box (OOB) Admin navigation layout and DOM structure. Users can now leverage the NavigationContextProvider alongside individual <Nav*> layout components to compose entirely custom sidebar or header shell layouts without needing to manually lift state or drill props. The default <Navigation> component continues to function identically without requiring downstream changes.

Key Changes:
  • State Extraction: Extracted all data fetching logic, cache interactions, and UI state (menuOpen, menuCollapsed, isHovered, filter) from the main component into a new NavigationContextProvider.

  • Granular Layout Building Blocks: Created atomic <Nav*> wrappers for each semantic layout section (<NavWrapper>, <NavMain>, <NavHeader>, <NavMenuWrapper>, <NavErrorSection>, <NavFooter>).

  • Layout V2 Alignment: Removed legacy branching logic from the new navigation wrappers, standardizing entirely on Layout V2 semantic HTML tags (<nav>, <section>, <header>, <aside>, <footer>).

  • OOB Preservation: Rebuilt the default <Navigation> component as a strict composition of these new atomic layout wrappers to ensure no regressions in default styling or DOM behavior.

  • Expanded Exports: Exported all atomic navigation blocks, types, contexts, and hooks to the root index layer for downstream consumption. All new wrappers are fully extensible via enableOverrides.

Example Custom Navigation
import {
  NavWrapper,
  NavMain,
  NavErrorSection,
  NavError,
  NavMenu,
  NavFooter,
  NavCollapser,
  NavMobileToggle,
  NavigationContextProvider,
  useNavigationContext
} from '@broadleaf/admin-components/dist/view';

export const CustomNavigation: React.FC = () => {
  const {
    isError,
    isSuccess,
    menuCollapsed,
    menuOpen,
    isHovered,
    filter,
    setFilter,
    setMenuOpen,
    setMenuCollapsed,
    setIsHovered,
    modules,
    fetchNavigation
  } = useNavigationContext();

  return (
    <NavigationContextProvider>
      <NavWrapper>
        <NavMain>
          {isError && (
            <NavErrorSection>
              <NavError fetchNavigation={fetchNavigation} />
            </NavErrorSection>
          )}

          {isSuccess && (
            <NavMenu
              filter={filter}
              modules={modules}
              setFilter={setFilter}
              setMenuOpen={setMenuOpen}
              menuCollapsed={menuCollapsed && !isHovered && !menuOpen}
            />
          )}
        </NavMain>

        <NavFooter>
          <NavCollapser
            menuCollapsed={menuCollapsed}
            setMenuCollapsed={val => {
              setMenuCollapsed(val);
              setMenuOpen(!val);
              setIsHovered(false);
            }}
          />
        </NavFooter>
      </NavWrapper>

      <NavMobileToggle
        menuOpen={menuOpen}
        setMenuOpen={val => {
          setMenuOpen(val);
          setMenuCollapsed(!val);
        }}
      />
    </NavigationContextProvider>
  );
};

Introduced support for an admin user to see and copy generated API key values

Important
Requires Metadata 2.0.9-GA (Release Trains 2.3.1, 2.2.3, 2.1.7) or 3.0.0-GA (Release Train 3.0.0).

Introduces support for a "One-Time Success Response" feature, providing a highly integrated framework for safely displaying sensitive, transient API response payloads (such as generated API keys) natively within the Admin UI. This feature looks for successResponse handler configurations on metadata actions. Upon successful form submission, it dynamically hooks-into the onSubmit promise resolution to present the read-only payload values in a slide-over modal or inline-replacement view before the transient state is discarded. It also includes setting a string field as "copyable" that triggers a "copy" action to render with the field and makes the field read-only.

While the successResponse handler can be configured for any action, a helper is included for Entity Create and Update views: submitSuccessResponse. This passes the configuration function down to the appropriate submit action in the metadata.

Example Usage of submitSuccessResponse
Views.entityViewCreate()
    .submitSuccessResponse(response -> response
        .title("Key Generated Successfully")
        .message("Copy this secure key now. It will not be shown again.")
        .displayMode(SuccessResponse.DisplayMode.REPLACE_CONTENT)
        // Renders the API response's 'secret' property as a click-to-copy string field
        .addField("secret", Fields.string()
            .copyable()
            .label("API Key")));

Unresolved directive in release-notes-2-1-0.adoc - include::release-notes-1-10-15.adoc[tag=1_10_15_enh_localstoragecache]

Miscellaneous

Unresolved directive in release-notes-2-1-0.adoc - include::release-notes-2-0-2.adoc[tag=2_0_2_enhancements] * Added support to ActionListGridCreateAction to configure the modal max size with the modalSize attribute. Supported values: * sm: 32rem (512 pixels) * lg: 48rem (768 pixels) * xl: 72rem (1152 pixels) * Refactored the ToggleSwitch component for improved keyboard accessibility and WAI-ARIA compliance Addressed: * Limited Keyboard Shortcuts: Previously, keyboard support was restricted to a simple spacebar toggle which acted as a circular index cycler. This made navigating back and forth through multiple choices laborious and did not meet standard accessibility expectations. * Double Screen-Reader Announcements: Because both the underlying input element and the visible label/button were interactive and present in the DOM tree, screen readers frequently exposed redundant or competing focused states. * Improper Aria Roles: The selection element lacked programmatic group context and checked-state markers, making color changes the only indicator of active options. * Updated EntityView useContextParams to take the React Router match param into account to share the behavior used by EntityLongView to determine which form within the view is active. * Added support for using EntityViews as targets of slide-over form actions (as opposed to modal forms) like ActionListGridUpdateViewAction that can target an existing View to render when invoked. * Added support for overriding some subcomponents of ListGrids blListGridHeaders for ListGridHeaders blActionListGridQueryFilter for ActionListGridQueryFilter * Ensured that empty EntityView form tabs are hidden automatically

Bug Fixes

Unresolved directive in release-notes-2-1-0.adoc - include::release-notes-1-10-15.adoc[tag=1_10_15_fixes] Unresolved directive in release-notes-2-1-0.adoc - include::release-notes-2-0-2.adoc[tag=2_0_2_fixes]