Migration Guide v7

AdminJS v7 comes with a lot of new features but also many potential breaking changes. If you're a developer that started working on an AdminJS project prior to the version 7 release, this guide should help you with the migration.

Compatibility List

Below you can find a list of AdminJS packages that are compatible with version 7 changes.

  • adminjs (7.x.x)

  • @adminjs/design-system (4.x.x)

  • @adminjs/express (6.x.x)

  • @adminjs/fastify (4.x.x)

  • @adminjs/hapi (7.x.x)

  • @adminjs/koa (4.x.x)

  • @adminjs/nestjs (6.x.x)

  • @adminjs/mikroorm (3.x.x)

  • @adminjs/mongoose (4.x.x)

  • @adminjs/objection (2.x.x)

  • @adminjs/prisma (4.x.x)

  • @adminjs/sequelize (4.x.x)

  • @adminjs/sql (2.x.x)

  • @adminjs/typeorm (5.x.x)

  • @adminjs/passwords (4.x.x)

  • @adminjs/logger (5.x.x)

  • @adminjs/import-export (3.x.x)

  • @adminjs/upload (4.x.x)

  • NEW @adminjs/themes (1.x.x)

ESM Support

With version 7 AdminJS has fully moved to ESM and will no longer support CJS projects.

We do not provide any specific migration tutorial for migrating to ESM, we suggest just searching for one - as long as your application is migrated to ESM, AdminJS should work without issues.

For Typescript developers, this migration might be easier since the amount of changes you have to make is much less when compared to vanilla CommonJS apps.

If you use @adminjs/nestjs do not update to ESM as NestJS doesn't support ESM as of 2023/04. Instead, please see the updated guide for NestJS plugin so that you can import updated AdminJS packages into your CJS NestJS app.

styled-components

AdminJS's design system is built with styled-components library which is still incompatible with ESM in its latest official release (5.3.9). Version 6 is still in beta - we attempted to use it and while it did work with ESM, it also did bring some additional issues. We've also attempted to use @emotion/styled library instead but it is also currently incompatible with ESM.

Eventually, we decided to stick to styled-components version 5.3.9. Our solution was to import the library, make necessary modifications and re-export it.

What this entails, is the modifications that you must make to your custom components:

  1. Do not use default import for styled. Use named import instead.

  2. Import from @adminjs/design-system/styled-components instead of styled-components

Before

import styled, { css } from 'styled-components'
import { Box } from '@adminjs/design-system'

const someCss = css`
  /* some css */
`

const StyledBox = styled(Box)`
  ${someCss}
  
  background: black;
`

// ...

After

import { styled, css } from '@adminjs/design-system/styled-components'
import { Box } from '@adminjs/design-system'

const someCss = css`
  /* some css */
`

const StyledBox = styled(Box)`
  ${someCss}
  
  background: black;
`

// ...

Please note that styled-components are exported from a separate namespace inside @adminjs/design-system to avoid mixing contexts.

Typescript developers might encounter issues with TS informing you that named exports (such as createGlobalStyle) cannot be found in @adminjs/design-system/styled-components. These exports are actually present and we could not pinpoint the exact issue which is causing that error to appear.

As a workaround, you can follow the steps below to resolve the problem.

Install @types/styled-components as a devDependency in your project:

$ yarn add -D @types/styled-components

Create a custom d.ts file which extends @adminjs/design-system/styled-components - for example ./vendor-types/adminjs-styled-components.d.ts:

declare module '@adminjs/design-system/styled-components' {
  export * from 'styled-components';
}

AdminJS.bundle removed

AdminJS.bundle has been deprecated since version 6. In version 7 it has been completely replaced with ComponentLoader. If you still haven't migrated to ComponentLoader , please see our custom components tutorial:

pageWriting your own Components

With version 7, we have also updated other @adminjs/* libraries. The libraries which bundle their own custom components now require you to pass your ComponentLoader instance, an example would be @adminjs/passwords

import { ComponentLoader } from 'adminjs'
import passwordsFeature from '@adminjs/passwords'

import { User } from './user.entity.js'

// ...

const componentLoader = new ComponentLoader()

const admin = new AdminJS({
  componentLoader,
  resources: [{
    resource: User,
    options: {},
    features: [passwordsFeature({
      componentLoader,
      // the rest of the feature's config
    })]
  }]
})

The example above applies to all other features which use ComponentLoader.

Overriding the Login page

Previously in order to override the Login page, you had to create an AdminJS instance and then use overrideLogin method:

admin.overrideLogin({ component: LoginComponent })

In version 7 all components are now overridden in the same way, the login page is no exception:

componentLoader.override('Login', <path to your login component>)

Prior to version 7, there were some additional drawbacks to overriding the Login page, mostly to it being Server Side Rendered, thus limiting your options for customization. In version 7, the Login page is a Single Page Application that allows you to modify the Login page freely.

Login page translations

The default Login page now uses different translation keys than before. All its translations have been moved from either messages, properties, labels to its own space in components section.

  "components": {
    "Login": {
      "welcomeHeader": "Welcome",
      "welcomeMessage": "to AdminJS - the world's leading open-source auto-generated admin panel for your Node.js application that allows you to manage all your data in one place",
      "properties": {
        "email": "Email",
        "password": "Password"
      },
      "loginButton": "Login"
    }
  },

To learn more about localization changes, please navigate to the Internationalization section:

pageInternationalization (i18n)

Design System

There are a few significant changes related to the design-system.

General changes

The main color (primary100) is set to buttons, links, avatar, navigation, table row selections, and illustrations.

All the changes can be found in our demo application (navigate to Pages > Design System Examples):

Changed values for variant and color props

To simplify theme customization in AdminJS we modified available props for Button and Box components from @adminjs/design-system. These props are: variant and color

Previously, you could use the following values for variant: primary, secondary, danger and success. These values were relevant to color. Currently, variant attribute modifies the appearance of a button: text, outlined, contained and light. Previous variant values have been moved to the new color attribute. Button default values are: variant="text" and color="primary".

Box variant attributes are: card, container, transparent, grey or white. The color attribute can be assigned similarly to how it's done with Button.

Before

<Button variant="primary"> 
  Click me
</Button>

After

<Button variant="contained" color="primary">
  Click me
</Button>

Disclaimer

There is a chance that some packages (React Select, Tip Tap) might not include type definitions. This is caused by their lack of support for ESM.

Localization

The biggest change to the internationalization feature is the removal of translations from the backend and moving it client-side. The backend is currently responsible for returning the payload with the translation codes. We have made a few changes in the locale object in the AdminJS config. The configuration has been expanded with a couple of new options:

  • availableLanguages

  • localeDetection

  • withBackend

AdminJS contains core translations in a few languages. The default language is set to en.

The locale object can be empty (null), however, if you want to provide the user with the ability to dynamically change the language, you will have to provide the availableLanguages key with an array of translations.

locale: { 
  language: 'pl', // default language
  availableLanguages: ['en', 'pl'], 
}

Users can use the last selected language stored in the browser cache by setting localeDetection variable to true,

locale: { 
  language: 'pl', 
  availableLanguages: ['en', 'pl'], 
  localeDetection: true, 
}

You can extend or change the default translations by passing the language translations object into locale config object. Below is a simple example:

locale: { 
  language: 'pl', 
  availableLanguages: ['en', 'pl'], 
  localeDetection: true, 
  translations: { 
    pl: { 
      messages: { 
        welcomeOnBoard_title: 'Nowy tytuł pulpitu', 
      }, 
    }, 
    en: { 
      messages: { 
        welcomeOnBoard_title: 'New dashboard title', 
      }, 
    }, 
  }, 
},

Translations groups

All the translation keys remain the same except for two new ones:

  • components - translations for components

  • pages - translations for custom pages

Translations in custom components

You can now dynamically translate custom components and pages with AdminJS config.

See the example based on a simple custom component:

// ... 
import { useTranslation } from 'adminjs'
// ...

const CustomComponent = (props) => {
    const { translateComponent } = useTranslation()
    return <div>{translateComponent('CustomComponent.textToTranslate')}</div>
}

You have to add translations to the component's namespace in our locale config to make this work.

const options = {
  // ...
  locale: {
    translations: {
      en: {
        components: {
          CustomComponent: {
            textToTranslate: 'This is text to translate'
          },
        }
      }
    }
  }
  // ...
}

If you would like to create custom messages to be handled by useNotice hook, you have to add our component section to the messages namespace. (use above options data)

import { useNotice } from "adminjs"
// ...
const sendNotice = useNotice()
// ...
sendNotice({
  message: 'Invalid "type" for relation',
  type: 'error',
})

If you want to use your own locale config you will have to change the translation object to be in line with the version 7 update. Below you find a simple example of what has to be changed (let's assume that the default language is pl and additional is en)

Before

locale: {
  language: 'pl',
  translations: {
    labels: {
      dashboard: 'Strona główna',
    }
  }
}

After

locale: {
  language: 'pl',
  availableLanguages: ['pl', 'en'],
  translations: {
    pl: {
      labels: {
        dashboard: 'Strona główna',
      }
    },
    en: {
      labels: {
        dashboard: 'Main page',
      }
    },
  }
}

@adminjs/bundler

@adminjs/bundler is a library which allows you to prebundle all AdminJS browser assets:

  • app.bundle.js

  • design-system.bundle.js

  • global.bundle.js

  • components.bundle.js

It is especially useful when you are deploying AdminJS to a server which doesn't grant you write access which AdminJS needs by default to create components.bundle.js which contains your custom components. @adminjs/bundler is a package which exports an in-code bundle script which you can use to solve the mentioned issue by:

  1. Pregenerating all bundle files.

  2. Serving them as static files on your server OR uploading them to a public storage such as AWS S3.

With the release of version 7, this package is now ESM-only and ComponentLoader support has been added. The script's usage has been simplified because we'd removed customComponentsInitializationFilePath and adminJsOptions. You now simply have to provide your componentLoader instance. Example:

import { bundle } from '@adminjs/bundler';

import componentLoader from './component-loader.js';

(async () => {
  const files = await bundle({
    componentLoader,
    destinationDir: 'public', // relative to CWD
  });
})();

Now either serve the contents of destinationDir publicly on your server, example for Express:

app.use(express.static(path.join(process.cwd(), 'public')));

or upload destinationDir contents to a storage of your choice (it has to be publicly accessible).

Now, remember to set assetsCDN option in your AdminJS configuration:

// ...
const admin = new AdminJS({
  // ...,
  assetsCDN: '<PUBLIC_ASSETS_URL>'
})

Demo Application

Our demo application at https://demo.adminjs.co/admin/login contains all the changes described above. If you're still struggling with the migration, you can take a look at a pull request which introduces these changes to our demo app: https://github.com/SoftwareBrothers/adminjs-example-app/pull/68

Last updated