Broadleaf Microservices
  • v1.0.0-latest-prod

Upgrading the Starter to AdminWeb 1.10.0

With the 1.10.0 release of AdminWeb, we upgraded the Tailwind dependency to v3 along with a few other enhancements. These require a few changes to AdminStarter.

Tailwind Upgrade Steps

  1. Update Admin Components, Styles, and Tailwindcss modules

    1. yarn install @broadleaf/admin-components@1.10.0-rc1.0 @broadleaf/admin-style@1.10.0-rc1.0 @broadleaf/admin-tailwindcss@1.10.0-rc1.0

  2. Update Tailwind, PostCSS, and Autoprefixer

    1. yarn install -D tailwindcss@latest postcss@latest autoprefixer@latest

    2. Tailwind v3 requires PostSS v8.

  3. Remove the global.css stylesheet from being imported by index.html

  4. Remove the purgecss comments in App.jsx as they are no longer needed

  5. Change the purge property in tailwind.config.js to content as well as defaultConfig.purge to defaultConfig.content

  6. Add a new file: styles/base.css

    1. The import order that should be use for tailwind vs the base Broadleaf styles has changed.

      @tailwind base;
      @tailwind components;
      @tailwind utilities;
      .tw-pointer-events-all {
        pointer-events: all;
  7. In styles/index.css

    1. Remove all tailwind imports, base, components, and utilities

    2. Import the new base.css before the broadleaf style imports

    3. Remove the /* purgecss start ignore */ comment at the top of the file, it is no longer needed

    4. After the existing broadleaf style imports, add a new one @import '@broadleaf/admin-components/styles/components/form-control.css';

    5. Remove the .tw-pointer-events-all style as it is in base.css now

    6. Add new style override:

      .sub-menu-open > .DropdownMenu > div > ul > li {
        @apply tw-cursor-default tw-bg-gray-800 tw-text-gray-700 tw-transition-colors;
  8. Remove the /* purgecss end ignore */ comment

That covers the essentials for upgrading to AdminWeb 1.10.0. Please also review Tailwind’s upgrade guide if you have additional style customizations.

Additional Updates

Add Build-Time Property Resolver Override

We noticed that properties defined in a .env at the root of the project are not always able to override the default properties defined in the @broadleaf/admin-components library.

To resolve this:
  1. Create a buildTimePropertyResolver.js in the src/ directory

    import { has, get } from 'lodash';
    import { utils } from '@broadleaf/admin-components';
     * Resolve properties from Vite at build time.
     * This overrides the default build-time properties resolver, but is still
     * subsequent to the runtime resolver.
    function buildTimeResolver(propertyName) {
      if (has(import.meta.env, propertyName)) {
        return get(import.meta.env, propertyName);
      // handle the case where we are evaluating this in Vite, which requires prefixing VITE_
      if (has(import.meta.env, `VITE_${propertyName}`)) {
        return get(import.meta.env, `VITE_${propertyName}`);
      return undefined;
    utils.Environment.registerResolver(buildTimeResolver, 2000);
  2. Import into src/index.tsx

    // ...
    import * as serviceWorker from './serviceWorker';
    import './loadLocaleData';
    import './componentRegistration';
    import './buildTimePropertyResolver'; // <-- add like this
    import '../styles/index.css';

Add Script to Build Translation Message Files (Optional)

This script will scan for any <component>.messages.js|ts files that contain react-intl message descriptors and generate a <local-code>.json file for each locale you intend to translate admin text into as well as a composite index.json. The index.json can be loaded into your app to override any out-of-box translations from the Broadleaf components library as well as include translations for new components.

This is the same process used by the Next.js Starter.
The <local-code>.json for the default language (e.g., en.json) can be sent to professional translation services to provide the contents for other languages, e.g., to create an fr.json.
  1. Add a few new dev-dependencies to facilitate the script

    1. yarn add -D colorette extract-react-intl-messages glob pretty-ms

  2. Create a scripts/build-langs.js

    const fs = require('fs');
    const colorette = require('colorette');
    const { sync: globSync } = require('glob');
    const extractMessages = require('extract-react-intl-messages');
    const last = require('lodash/last');
    const ms = require('pretty-ms');
    const MESSAGES_PATTERN = 'src/**/*?(.)messages.{js,ts}';
    const DEFAULT_LOCALE = 'en';
    const TRANSLATED_LOCALES = []; // e.g., ['fr', 'es']
    const LANG_DIR = 'messages/';
    const LANG_PATTERN = `${LANG_DIR}*.json`;
    async function buildLangs() {
      const start =;
      const mainFile = `${LANG_DIR}index.json`;
          `\n${colorette.bold(MESSAGES_PATTERN)} → ${colorette.bold(
      // extract the default messages that from our components messages files
      await extractMessages(
        { defaultLocale: DEFAULT_LOCALE }
      // merge the existing translated json files (e.g. es.json, fr.json, etc) into
      // one object so they can be aggregated
      const allMessages = globSync(LANG_PATTERN)
        .filter(filename => !filename.endsWith('index.json'))
        .map(filename => {
          const locale = last(filename.split('/')).split('.json')[0];
          return { [locale]: JSON.parse(fs.readFileSync(filename, 'utf8')) };
        .reduce((acc, localeObject) => {
          return { ...acc, ...localeObject };
        }, {});
      // write the index.json aggregated json file with all of the messages
      fs.writeFileSync(mainFile, JSON.stringify({ ...allMessages }, null, 2));
          `created ${colorette.bold(`${mainFile}`)} in ${colorette.bold(
            ms( - start)
  3. Modify DEFAULT_LOCAL and TRANSLATED_LOCALES as desired for your project

  4. Use the build-langs script in your package.json

      "scripts": {
        "build:langs": "node scripts/build-langs.js"
  5. Run yarn build:langs

    1. This will generate a (likely) empty messages/en.json and messages/index.json

  6. Import the new messages into your App.jsx

  7. Merge with the base Broadleaf messages

  8. Pass to <AdminProvider>

    import merge from 'lodash/merge'; // optionally using lodash for ease of merging
    import messages from '@broadleaf/admin-components/messages';
    import customMessages from '../messages';
    const combinedMessages = merge({}, messages, customMessages);
    // ...
    <AdminProvider messages={combinedMessages}>