11 March 2026
Composable Commerce
Merchant Center Customizations
Announcement
Development
The Application Kit packages have been released with a new major version v27.

This guide provides detailed information about the migration process and necessary changes for Custom Applications and Custom Views to ensure a successful migration.

This release contains breaking changes. You must upgrade to ESLint 9 and replace your legacy .eslintrc.* config file (for example, .eslintrc.js, .eslintrc.cjs, or .eslintrc.json) with eslint.config.js.
Note that the following packages ship a migrations/ directory with a version-specific upgrade guide available locally after installing:
  • node_modules/@commercetools-frontend/eslint-config-mc-app/migrations/v27.md
  • node_modules/@commercetools-backend/eslint-config-node/migrations/v27.md

These guides work as plain documentation or can be handed directly to an AI coding assistant:

Migrate my ESLint config following node_modules/@commercetools-frontend/eslint-config-mc-app/migrations/v27.md

Preparation

Config file

Replace your .eslintrc.js file with eslint.config.js. The flat config format exports an array of config objects rather than a single object with extends and overrides.
// BEFORE (.eslintrc.js)
module.exports = {
  root: true,
  extends: ['@commercetools-frontend/eslint-config-mc-app'],
  overrides: [
    {
      files: ['**/*.ts', '**/*.tsx'],
      rules: { /* ... */ },
    },
  ],
};

// AFTER (eslint.config.js)
const mcAppConfig = require('@commercetools-frontend/eslint-config-mc-app');

module.exports = [
  ...mcAppConfig,
  {
    files: ['**/*.ts', '**/*.tsx'],
    rules: { /* ... */ },
  },
];

Plugins

Flat config requires actual plugin objects rather than string-based plugin names.

// BEFORE
module.exports = {
  plugins: ['import', 'jsx-a11y'],
};

// AFTER
const importPlugin = require('eslint-plugin-import');
const jsxA11yPlugin = require('eslint-plugin-jsx-a11y');

module.exports = [
  {
    plugins: {
      import: importPlugin,
      'jsx-a11y': jsxA11yPlugin,
    },
  },
];

Parser

The parser is now specified in languageOptions as an imported module rather than a string path.
// BEFORE
module.exports = {
  parser: '@babel/eslint-parser',
};

// AFTER
const babelParser = require('@babel/eslint-parser');

module.exports = [
  {
    languageOptions: {
      parser: babelParser,
    },
  },
];

Globals

The env property is replaced by the globals package spread into languageOptions.globals.
// BEFORE
module.exports = {
  env: { browser: true, node: true, jest: true },
};

// AFTER
const globals = require('globals');

module.exports = [
  {
    languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.node,
        // Jest globals are now automatically scoped to test files
        // (**/*.{spec,test}.*) by the config package.
      },
    },
  },
];

Ignore patterns

The .eslintignore file is no longer supported. Move ignore patterns directly into eslint.config.js.
// BEFORE (.eslintignore)
dist/
coverage/
*.generated.js

// AFTER (eslint.config.js)
module.exports = [
  {
    ignores: ['dist/', 'coverage/', '*.generated.js'],
  },
  // ...rest of config
];

Dependency upgrades

The following dependencies were updated as part of this migration:

PackageBeforeAfterReason
@typescript-eslint/*^5.62.0^8.0.0v5 incompatible with ESLint 9 plugin API
eslint-plugin-jest^27.2.3^28.0.0v27 uses deprecated APIs removed in ESLint 9
eslint-plugin-react-hooks^4.6.0^5.0.0v4 designed for ESLint 8 only
eslint-plugin-testing-library^5.11.1^7.0.0v7 uses @typescript-eslint/utils v8
globals^15.15.0Required for explicit global definitions in flat config
@rushstack/eslint-patchpresentremovedESLint 8 workaround, not needed in ESLint 9

Migration warnings

If your project still uses a legacy .eslintrc.* file, the config packages will print an actionable warning at load time pointing to the migration guide. Projects already using eslint.config.js will see no output.