Skip to main content

Migrating from v7 to v8

Use the upgrade scripts

Start by executing the upgrade script in the root of your project:

npx @comet/upgrade@latest v8

That handles most of the necessary changes.

The following sections go over all necessary changes. All changes handled by the upgrade script are hidden in closed accordions. Refer to the hidden content if you face issues with the upgrade scripts.

info

You can re-execute individual upgrade scripts if needed: npx @comet/upgrade@latest v8/[upgrade-script-name].ts

General

✅ Upgrade Node to v22

Handled by @comet/upgrade

In development:

Handled by following upgrade script
npx @comet/upgrade v8/replace-node-with-v22-locally.ts
- 20
+ 22
- "@types/node": "^20.0.0",
+ "@types/node": "^22.0.0",

In pipeline and deployment:

Handled by following upgrade script
npx @comet/upgrade v8/replace-node-with-v22-in-gitlab-ci-files.ts

Make sure you use Node 22 in your CI files. When using Gitlab CI, check all files in the .gitlab-ci folders. Make sure to extend the correct jobs and replace all images and base images.

- extends: .lint-npm-node20
+ extends: .lint-npm-node22

- BASE_IMAGE: "ubi/s2i-ubi9-nodejs20-minimal"
+ BASE_IMAGE: "ubi/s2i-ubi9-nodejs22-minimal"

- image: eu.gcr.io/vivid-planet/utils/ubi9-nodejs20-minimal:master
+ image: eu.gcr.io/vivid-planet/utils/ubi9-nodejs22-minimal:master

API

✅ Upgrade peer dependencies

NestJS

  1. Upgrade all your dependencies to support NestJS v11

    Handled by @comet/upgrade
    Handled by following upgrade script
    npx @comet/upgrade v8/update-nest-dependencies.ts
    {
    "dependencies": {
    + "@apollo/server": "^4.0.0",
    - "@nestjs/apollo": "^10.0.0",
    - "@nestjs/common": "^9.0.0",
    - "@nestjs/config": "^2.0.0",
    - "@nestjs/core": "^9.0.0",
    - "@nestjs/graphql": "^10.0.0",
    - "@nestjs/passport": "^9.0.0",
    - "@nestjs/platform-express": "^9.0.0",
    + "@nestjs/apollo": "^13.0.0",
    + "@nestjs/common": "^11.0.0",
    + "@nestjs/core": "^11.0.0",
    + "@nestjs/graphql": "^13.0.0",
    + "@nestjs/passport": "^11.0.0",
    + "@nestjs/platform-express": "^11.0.0",
    - "apollo-server-core": "^3.0.0",
    - "apollo-server-express": "^3.0.0",
    - "express": "^4.0.0",
    + "express": "^5.0.0",
    - "graphql": "^15.0.0",
    + "graphql": "^16.10.0",
    },
    "devDependencies": {
    - "@nestjs/cli": "^9.0.0",
    - "@nestjs/schematics": "^9.0.0",
    - "@nestjs/testing": "^9.0.0",
    + "@nestjs/cli": "^11.0.0",
    + "@nestjs/schematics": "^11.0.0",
    + "@nestjs/testing": "^11.0.0",
    - "@types/express": "^4.0.0",
    + "@types/express": "^5.0.0",
    }
    }
  2. Update the custom formatError function to hide GraphQL field suggestions

    Handled by @comet/upgrade
    Handled by following upgrade script
    npx @comet/upgrade v8/update-graphql-format-error.ts
    - import { ValidationError } from "apollo-server-express";
    + import { ValidationError } from "@nestjs/apollo";

    /* ... */

    GraphQLModule.forRootAsync<ApolloDriverConfig>({
    /* ... */
    useFactory: (moduleRef: ModuleRef) => ({
    /* ... */,
    formatError: (error) => {
    // Disable GraphQL field suggestions in production
    if (process.env.NODE_ENV !== "development") {
    - if (error instanceof ValidationError) {
    + if (error.extensions?.code === "GRAPHQL_VALIDATION_FAILED") {
    return new ValidationError("Invalid request.");
    }
    }
    return error;
    },

    }),
    }),
  3. You may need to update some of your routes to support Express v5. See the migration guide for more information.

✅ Add NestJS peer dependencies

Peer dependencies defined by NestJS have been added as peer dependencies to @comet/cms-api.

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/nest-peer-dependencies.ts

To upgrade, install the dependencies in your project:

{
"dependencies": {
+ "class-transformer": "^0.5.1",
- "reflect-metadata": "^0.1.13",
+ "reflect-metadata": "^0.2.2",
- "rxjs": "^7.0.0",
+ "rxjs": "^7.8.1",
}
}

MikroORM

  1. Upgrade all your dependencies:

    Handled by @comet/upgrade
    Handled by following upgrade script
    npx @comet/upgrade v8/update-mikro-orm-dependencies.ts
    {
    "dependencies": {
    - "@mikro-orm/cli": "^5.9.8",
    - "@mikro-orm/core": "^5.9.8",
    - "@mikro-orm/migrations": "^5.9.8",
    - "@mikro-orm/nestjs": "^5.2.3",
    - "@mikro-orm/postgresql": "^5.9.8",
    + "@mikro-orm/cli": "^6.4.0",
    + "@mikro-orm/core": "^6.4.0",
    + "@mikro-orm/migrations": "^6.4.0",
    + "@mikro-orm/nestjs": "^6.0.2",
    + "@mikro-orm/postgresql": "^6.4.0",
    },
    }
  2. Follow the official migration guide to upgrade.

    Partially handled by upgrade scripts

    We provide upgrade scripts for basic migrations. Please note that these scripts might not cover all necessary migrations.

    Changes handled by @comet/upgrade

    Remove generic from BaseEntity:

    npx @comet/upgrade v8/mikro-orm-base-entity-generic.ts

    Rename customType to type:

    npx @comet/upgrade v8/mikro-orm-custom-type.ts

    Rename onDelete to deleteRule:

    npx @comet/upgrade v8/mikro-orm-delete-rule.ts

    Add a mikro-orm script with a dotenv call to package.json:

    npx @comet/upgrade v8/mikro-orm-dotenv.ts

    Change all imports from @mikro-orm/core to @mikro-orm/postgresql:

    npx @comet/upgrade v8/mikro-orm-imports.ts

    Wrap config in defineConfig:

    npx @comet/upgrade v8/mikro-orm-ormconfig.ts

✅ class-validator

The class-validator peer dependency has been bumped to v0.14.0.

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/update-class-validator.ts
{
"dependencies": {
- "class-validator": "0.13.2",
+ "class-validator": "^0.14.0",
}
}

✅ Sentry

The Sentry dependency has been bumped to v9.

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/update-sentry.ts
  1. Upgrade the "@sentry/node" dependency in your package.json file:
{
"dependencies": {
- "@sentry/node": "^7.0.0",
+ "@sentry/node": "^9.0.0",
},
}
  1. Update your main.ts file to remove all Sentry.Handlers and add Sentry.setupExpressErrorHandler(app):
-   app.use(Sentry.Handlers.requestHandler());
- app.use(Sentry.Handlers.tracingHandler());
- app.use(Sentry.Handlers.errorHandler());
+ Sentry.setupExpressErrorHandler(app);

None of the other breaking changes in @sentry/node should affect us. If you still encounter problems, consult the official migration guides:

@kubernetes/client-node

The @kubernetes/client-node peer dependency has been bumped to v1.

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/update-kubernetes-client-node.ts
{
"dependencies": {
- "@kubernetes/client-node": "^0.18.0",
+ "@kubernetes/client-node": "^1.0.0",
}
}

✅ Add new package @comet/api-generator

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/api-generator-dev-dependencies.ts

The API Generator has been moved into a separate package @comet/api-generator.

api/package.json
devDependencies: {
+ "@comet/api-generator": "^8.0.0",
}

API Generator - Removed Special status Field Behavior

Previously, if entities specified a status enum, it was automatically added to list queries arguments with a default value.

This special handling has been removed. The status field now behaves like a normal enum. Filtering by status can be done with the normal filtering mechanism.

✅ Remove @comet/blocks-api

The @comet/blocks-api package has been merged into the @comet/cms-api package.

Handled by @comet/upgrade

To upgrade, perform the following steps:

  1. Remove the package:

    Handled by following upgrade script
    npx @comet/upgrade v8/remove-blocks-packages.ts
    api/package.json
    - "@comet/blocks-api": "^7.x.x",
  2. Update all your imports from @comet/blocks-api to @comet/cms-api

    Handled by following upgrade script
    npx @comet/upgrade v8/merge-blocks-api-into-cms-api.ts
  3. Update imports that have been renamed

    Handled by following upgrade script
    npx @comet/upgrade v8/merge-blocks-api-into-cms-api.ts
  4. Remove usages of removed export getFieldKeys (probably none)

Replace nestjs-console with nest-commander

The nestjs-console package isn't actively maintained anymore. We therefore replace it with nest-command.

The upgrade script will remove the nestjs-console package and install nest-commander and @types/inquirer.

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/replace-nestjs-console-with-nest-commander.ts
  1. Uninstall nestjs-console
  2. Install nest-commander and @types/inquirer

You have to perform the following steps manually:

  1. Update api/src/console.ts to use nest-commander. Minimum example:

    import { CommandFactory } from "nest-commander";

    import { AppModule } from "./app.module";
    import { createConfig } from "./config/config";

    const config = createConfig(process.env);

    async function bootstrap() {
    const appModule = AppModule.forRoot(config);

    // @ts-expect-error CommandFactory doesn't except DynamicModule, only Type<any>
    await CommandFactory.run(appModule, {
    logger: ["error", "warn", "log"],
    serviceErrorHandler: async (error) => {
    console.error(error);
    process.exit(1);
    },
    });
    }

    bootstrap();
  2. Update your commands to the new nest-commander syntax

Migrating nestjs-console commands to nest-commander

This section highlights the necessary changes to convert a nestjs-console command to nest-commander.

  1. Replace the @Console() decorator with @Command():

    - import { Command, Console } from "nestjs-console";
    + import { Command } from "nest-commander";

    - @Injectable()
    - @Console()
    + @Command({
    + name: "fixtures",
    + description: "Create fixtures with faker.js",
    + })
    export class FixturesConsole {
    - @Command({
    - command: "fixtures",
    - description: "Create fixtures with faker.js",
    - })
    @CreateRequestContext()
    async execute(): Promise<void> {
    /* ... */
    }
    }
  2. Extend CommandRunner:

    + import { CommandRunner } from "nest-commander";

    - export class FixturesConsole {
    + export class FixturesConsole extends CommandRunner {
    /* ... */
    }
  3. Add a super() call to the constructor:

    export class FixturesConsole extends CommandRunner {
    constructor(@Inject(CONFIG) private readonly config: Config) {
    + super();
    }
    }
  4. Rename the executing function to run:

    export class FixturesConsole extends CommandRunner {
    @CreateRequestContext()
    - async execute(): Promise<void> {
    + async run(): Promise<void> {
    /* ... */
    }
    }
  5. If necessary, migrate arguments and options.

    Arguments: Move from command field into arguments field:

    import-redirects.command.ts
    @Command({
    name: "import-redirects",
    + arguments: "<filepath> [comment]",
    description: "Import redirects from a CSV file",
    })
    export class ImportRedirectsCommand extends CommandRunner {
    @Command({
    - command: "import-redirects [filepath] [comment]",
    description: "Import redirects from csv file",
    })
    @CreateRequestContext()
    - async execute(filepath: string, comment = "Imported"): Promise<void> {
    + async run([filepath, comment = "Imported"]: string[]): Promise<void> {
    /* ... */
    }
    }

    Options: Use the @Option() decorator:

    refresh-block-index-views.command.ts
    @Command({
    name: "refreshBlockIndexViews",
    })
    export class RefreshBlockIndexViewsCommand extends CommandRunner {
    @Command({
    command: "refreshBlockIndexViews",
    - options: [
    - {
    - flags: "-f, --force [boolean]",
    - defaultValue: false,
    - },
    - ],
    })
    @CreateRequestContext()
    - async refreshBlockIndexViews(args: { force: boolean }): Promise<void> {
    + async run(arguments: string[], options: { force?: true }): Promise<void> {
    /* ... */
    }

    + @Option({
    + flags: "-f, --force",
    + })
    + parseForce() {}
    }

    Review the documentation for more information.

  6. Optional: Rename console to command:

    - export class FixturesConsole extends CommandRunner {
    + export class FixturesCommand extends CommandRunner {
    /* ... */
    }

Remove passport

The passport dependencies were removed by the upgrade script.

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/remove-passport.ts

Remove all passport-dependencies and add @nestjs/jwt

{
"dependencies": {
- "@nestjs/passport": "^9.0.0",
- ...other passport dependencies
+ "@nestjs/jwt": "^10.2.0",
}
}

Following steps must be done manually:

Rename the strategy-factories and wrap them in ...createAuthGuardProviders():

-   createStaticCredentialsBasicStrategy({ ... }),
- createAuthProxyJwtStrategy({ ... }),
- createStaticCredentialsBasicStrategy({ ... }),
+ ...createAuthGuardProviders(
+ createBasicAuthService({ ... }),
+ createJwtAuthService({ ... }),
+ createSitePreviewAuthService({ ... }),
+ createStaticUserAuthService({ ... }),
+ ),
Configuration changes

The configuration of the AuthServices have changed slightly compared to the strategies, however they remain similar. Consulting the code completion should help to adapt.

Replace createAuthResolver with the class name:

-   useClass: createCometAuthGuard([...]),
+ useClass: CometAuthGuard,
Passport not supported anymore

CometAuthGuard does not support Passport strategies anymore. Consider rewriting or wrapping into AuthServiceInterface. However, you still can use passport strategies in conjunction with the provided AuthGuard from @nestjs/passport.

Import JwtModule from @nestjs/jwt:

    exports: [UserService, AccessControlService],
+ imports: [JwtModule],

Admin

Upgrade peer dependencies

React

The React dependency has been bumped to v18.

  1. Upgrade all your dependencies

    Handled by @comet/upgrade
    Handled by following upgrade script
    npx @comet/upgrade v8/update-react-dependencies.ts
    {
    "dependencies": {
    - "react": "^17.0.2",
    - "react-dom": "^17.0.2",
    + "react": "^18.3.1",
    + "react-dom": "^18.3.1"
    },
    "devDependencies": {
    - "@types/react": "^17.0.83",
    - "@types/react-dom": "^17.0.26",
    + "@types/react": "^18.3.18",
    + "@types/react-dom": "^18.3.5"
    }
    }
  2. Follow the official migration guide to upgrade.

    tip

    Use types-react-codemod to fix potential TypeScript compile errors when upgrading to @types/react@^18.0.0.

MUI

The MUI dependencies (@mui/material, @mui/system, @mui/utils, @mui/icons-material, @mui/lab) were bumped to v7.

  1. Upgrade your MUI dependencies

    Handled by @comet/upgrade
    Handled by following upgrade script
    npx @comet/upgrade v8/update-mui-dependencies.ts
    -       "@mui/icons-material": "^5.0.0",
    - "@mui/lab": "^5.0.0-alpha.76",
    - "@mui/material": "^5.0.0",
    - "@mui/system": "^5.0.0",
    - "@mui/utils": "^5.0.0",
    + "@mui/icons-material": "^7.0.0",
    + "@mui/lab": "^7.0.0-beta.9",
    + "@mui/material": "^7.0.0",
    + "@mui/system": "^7.0.0",
    + "@mui/utils": "^7.0.0",
  2. Execute MUI codemods to update your code

    Handled by @comet/upgrade
    Handled by following upgrade script
    npx @comet/upgrade v8/mui-codemods.ts
  3. Follow the official migration guides to upgrade:

MUI X (DataGrid)

The MUI dependencies (@mui/x-data-grid, @mui/x-data-grid-pro) were bumped to v7.

Handled by @comet/upgrade

In package.json update the version of the MUI X packages to ^7.22.3.

Handled by following upgrade script
npx @comet/upgrade v8/update-mui-x-dependencies.ts
- "@mui/x-data-grid": "^5.x.x",
- "@mui/x-data-grid-pro": "^5.x.x",
- "@mui/x-data-grid-premium": "^5.x.x",

+ "@mui/x-data-grid": "^7.22.3",
+ "@mui/x-data-grid-pro": "^7.22.3",
+ "@mui/x-data-grid-premium": "^7.22.3",

A lots of props have been renamed from MUI, for a detailed look, see the official migration guide v5 -> v6 and migration guide v6 -> v7. There is also a codemod from MUI which handles most of the changes:

Handled by following upgrade script
npx @comet/upgrade v8/mui-x-codemods.ts
warning

Be aware if you have a date in the data grid, you will need to add a valueGetter

    <DataGrid
//other props
columns=[
{
field: "updatedAt",
type: "dateTime",
+ valueGetter: (params, row) => row.updatedAt && new Date(row.updatedAt)
}]
/>

Also, be aware if you have a valueGetter or valueFormatter in the data grid, you will need to change the arguments passing to the functions. Previously, arguments were passed as an object. Now, they are passed directly as individual parameters

    <DataGrid
//other props
columns=[
{
field: "updatedAt",
type: "dateTime",
- valueGetter: ({params, row}) => row.updatedAt && new Date(row.updatedAt)
+ valueGetter: (params, row) => row.updatedAt && new Date(row.updatedAt)
- valueFormatter: ({value}) => (value ? intl.formatDate(value, { dateStyle: "medium", timeStyle: "short" }) : ""),
+ valueFormatter: (value) => (value ? intl.formatDate(value, { dateStyle: "medium", timeStyle: "short" }) : ""),
}]
/>

✅ Vite / SWC

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/update-swc-dependencies.ts
-        "@swc/plugin-emotion": "^3.0.13",
- "@vitejs/plugin-react-swc": "^3.7.2",
+ "@swc/plugin-emotion": "^8.7.2",
+ "@vitejs/plugin-react-swc": "^3.8.0",

✅ Add new package @comet/admin-generator

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/admin-generator-dev-dependencies.ts

The Admin Generator has been moved into a separate package @comet/admin-generator.

admin/package.json
devDependencies: {
+ "@comet/admin-generator": "^8.0.0",
}

✅ Remove @comet/blocks-admin

The @comet/blocks-admin package has been merged into the @comet/cms-admin package.

Handled by @comet/upgrade

To upgrade, perform the following steps:

  1. Remove the package:

    Handled by following upgrade script
    npx @comet/upgrade v8/remove-blocks-packages.ts
    admin/package.json
    - "@comet/blocks-admin": "^7.x.x",
  2. Update all your imports from @comet/blocks-admin to @comet/cms-admin

    Handled by following upgrade script
    npx @comet/upgrade v8/merge-blocks-admin-into-cms-admin.ts
  3. Update imports that have been renamed

    Handled by following upgrade script
    npx @comet/upgrade v8/merge-blocks-admin-into-cms-admin.ts

Manually remove usages of removed exports CannotPasteBlockDialog, ClipboardContent, useBlockClipboard, Collapsible, CollapsibleSwitchButtonHeader, usePromise, DispatchSetStateAction, SetStateAction, and SetStateFn

tip

Use Dispatch<SetStateAction<T>> from react instead of DispatchSetStateAction.

✅ Remove @comet/admin-theme

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/merge-admin-theme-into-admin.ts
npx @comet/upgrade v8/remove-admin-theme-package.ts

The @comet/admin-theme package has been merged into @comet/admin, adjust the imports accordingly:

- import { createCometTheme } from "@comet/admin-theme";
+ import { createCometTheme } from "@comet/admin";

const theme = createCometTheme();

✅ Remove @comet/admin-react-select

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/remove-comet-admin-react-select-dependency.ts
- "@comet/admin-react-select": "^7.x.x",

It is recommended to use the AutocompleteField or the SelectField components from @comet/admin instead:

- import { FinalFormReactSelectStaticOptions } from "@comet/admin-react-select";
- <Field name="color" type="text" component={FinalFormReactSelectStaticOptions} fullWidth options={options} />;
+ import { AutocompleteField } from "@comet/admin";
+ <AutocompleteField name="color" label="Color" options={options} fullWidth />;

✅ Merge providers into CometConfigProvider

The separate providers for CMS features (e.g, DamConfigProvider) have been merged into a CometConfigProvider.

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/comet-config-provider.ts

Note: This upgrade script is experimental and might not work as expected in your application. Review the result carefully.

Wrap your application with the CometConfigProvider:

src/App.tsx
import { CometConfigProvider } from "@comet/cms-admin";

function App() {
return (
<CometConfigProvider
apiUrl={config.apiUrl}
graphQLApiUrl={`${config.apiUrl}/graphql`}
adminUrl={config.adminUrl}
>
{/* Application */}
</CometConfigProvider>
);
}

Move module configs to the new provider:

 <CometConfigProvider
apiUrl={config.apiUrl}
graphQLApiUrl={`${config.apiUrl}/graphql`}
adminUrl={config.adminUrl}
+ dam={{
+ ...config.dam,
+ scopeParts: ["domain"],
+ contentGeneration: {
+ generateAltText: true,
+ generateImageTitle: true,
+ },
+ }}
>
{/* Application */}
</CometConfigProvider>

Remove the old config providers:

- <DamConfigProvider>
{/* Application */}
- </DamConfigProvider>

Update usages of renamed exports:

  • useSitesConfig() -> useSiteConfigs()
  • useLocale() -> useContentLanguage()
  • useCmsBlockContext() -> useBlockContext()

Remove the allCategories prop from PagesPage:

 <PagesPage
path="/pages/pagetree/main-navigation"
- allCategories={pageTreeCategories}
documentTypes={pageTreeDocumentTypes}
category="MainNavigation"
renderContentScopeIndicator={(scope) => <ContentScopeIndicator scope={scope} />}
/>
Experimental upgrade script

This upgrade script is experimental and might not work as expected in your application. Review the result carefully.

Add proxy for /dam URLs

The API now only returns relative URLs for DAM assets. You must proxy the /dam URLs in your application to the API. This must be done for local development and production.

In development:

Add the proxy to your vite config:

//...
server: {
// ...
proxy: process.env.API_URL_INTERNAL
? {
"/dam": {
target: process.env.API_URL_INTERNAL,
changeOrigin: true,
secure: false,
},
}
: undefined,
// ...
},
//...

In production:

Add the proxy to your admin server:

"dependencies": {
// ...
+ "http-proxy-middleware": "^3.0.3"
// ...
},
// ...

app.get("/status/health", (req, res) => {
// ...
});

+ const proxyMiddleware = createProxyMiddleware({
+ target: process.env.API_URL_INTERNAL + "/dam",
+ changeOrigin: true,
+ });
+ app.use("/dam", proxyMiddleware);

// ...

You might also need to add API_URL_INTERNAL to your values.tpl.yaml for deployment:

admin:
env:
ADMIN_URL: "https://$ADMIN_DOMAIN"
API_URL: "https://$ADMIN_DOMAIN/api"
+ API_URL_INTERNAL: "http://$APP_NAME-$APP_ENV-api:3000/api"
Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/rename-menu-components-in-admin.ts

To better differentiate between imports from @comet/admin and @mui/material, the following components and related types have been renamed:

  • MenuMainNavigation
  • MenuPropsMainNavigationProps
  • MenuClassKeyMainNavigationClassKey
  • MenuItemMainNavigationItem
  • MenuItemPropsMainNavigationItemProps
  • MenuItemClassKeyMainNavigationItemClassKey
  • MenuCollapsibleItemMainNavigationCollapsibleItem
  • MenuCollapsibleItemPropsMainNavigationCollapsibleItemProps
  • MenuCollapsibleItemClassKeyMainNavigationCollapsibleItemClassKey
  • IWithMenuWithMainNavigation
  • withMenuwithMainNavigation
  • MenuItemAnchorLinkMainNavigationItemAnchorLink
  • MenuItemAnchorLinkPropsMainNavigationItemAnchorLinkProps
  • MenuItemGroupMainNavigationItemGroup
  • MenuItemGroupClassKeyMainNavigationItemGroupClassKey
  • MenuItemGroupPropsMainNavigationItemGroupProps
  • MenuItemRouterLinkMainNavigationItemRouterLink
  • MenuItemRouterLinkPropsMainNavigationItemRouterLinkProps

The MenuContext has been removed, use the new useMainNavigation hook instead.

Stay on same page after changing scope

The Admin now stays on the same page per default when changing scopes. Perform the following changes:

  1. Remove the path prop from the PagesPage component

    admin/src/common/MasterMenu.tsx
    <PagesPage
    - path="/pages/pagetree/main-navigation"
    allCategories={pageTreeCategories}
    documentTypes={pageTreeDocumentTypes}
    category="MainNavigation"
    renderContentScopeIndicator={(scope) => <ContentScopeIndicator scope={scope} />}
    />
  2. Remove the redirectPathAfterChange prop from the RedirectsPage component

    admin/src/common/MasterMenu.tsx
    {
    type: "route",
    primary: <FormattedMessage id="menu.redirects" defaultMessage="Redirects" />,
    route: {
    path: "/system/redirects",
    - render: () => <RedirectsPage redirectPathAfterChange="/system/redirects" />,
    + component: RedirectsPage
    },
    requiredPermission: "pageTree",
    },
  3. Optional: Remove unnecessary usages of the useContentScopeConfig hook

    export function ProductsPage() {
    const intl = useIntl();

    - useContentScopeConfig({ redirectPathAfterChange: "/structured-content/products" });
    }

Update usage of DataGridToolbar

DataGridToolbar has been simplified to a basic wrapper component. Props and features from the standard Toolbar component have been removed, along with the density prop since density is now controlled by the DataGrid.

The new usage simplifies the component structure - children can now be passed directly without needing to wrap them in ToolbarItem and ToolbarActions components:

- <DataGridToolbar density="compact">
+ <DataGridToolbar>
- <ToolbarItem>
<GridToolbarQuickFilter />
- </ToolbarItem>
- <ToolbarItem>
<GridFilterButton />
- </ToolbarItem>
- <ToolbarItem>
<GridColumnsButton />
- </ToolbarItem>
<FillSpace />
- <ToolbarActions>
<Button responsive variant="outlined">
Secondary action
</Button>
<Button responsive startIcon={<AddIcon />}>
Add item
</Button>
- </ToolbarActions>
</DataGridToolbar>

✅ Pass columns instead of apiRef to muiGridSortToGql Function

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/mui-grid-sort-to-gql.ts

Note: This upgrade script will naively change the second argument of muiGridSortToGql function to columns, assuming that columns is available in the current scope.

The muiGridSortToGql helper now expects the columns instead of the apiRef:

const columns : GridColDef[] = [/* column definitions*/];
const dataGridRemote = useDataGridRemote();
const persistentColumnState = usePersistentColumnState("persistent_column_state");

- muiGridSortToGql(dataGridRemote.sortModel, persistentColumnState.apiRef);
+ muiGridSortToGql(dataGridRemote.sortModel, columns);
Naive upgrade script

This upgrade script will naively change the second argument of muiGridSortToGql function to columns, assuming that columns is available in the current scope.

Remove error prop from DataGrid

The error and onError props were removed - the grid no longer catches errors during rendering. To catch errors that happen during rendering use the error boundary. The components.ErrorOverlay slot was also removed.

MUI migration guide

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/mui-data-grid-remove-error-prop.ts

Note: Error handling must be implemented manually, the upgrade script simply removes all usages of the error prop on DataGrids and adds a TODO: comment.

Error handling must be implemented manually

This upgrade script simply removes all usages of the error prop on DataGrids and adds a TODO: comment. Error handling must be implemented manually.

The recommended way to handle errors is to use the ErrorBoundary in the parent component and throw errors where the query error happens.

- const { loading, data, error } = useQuery(/* query parameters */)
- <DataGrid error={error} /* other props */ >

+ const { loading, data, error } = useQuery(/* query parameters */)
+ if (error) {
+ throw error
+ }
+ <DataGrid /* other props */ >

useDataGridRemote Hook - Return Value

The useDataGridRemote hook has been changed to match the updated DataGrid props:

- const { pageSize, page, onPageSizeChange } = useDataGridRemote();
+ const { paginationModel, onPaginationModelChange } = useDataGridRemote();
rowCount must be passed

Be aware that you must pass rowCount to the DataGrid when using the useDataGridRemote hook. Otherwise, the pagination component will show a NaN value when used with server-side pagination.

✅ Import Dialog from @comet/admin package

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/update-import-of-dialog.ts
- import { Dialog } from "@mui/material";
+ import { Dialog } from "@comet/admin";

✅ Add DialogContent to EditDialog

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/add-dialog-content-to-edit-dialog.ts

The DialogContent inside EditDialog has been removed. To maintain the existing styling of EditDialog, such as for forms and text, manually wrap the content with DialogContent to ensure proper spacing. For grids or other elements that already handle their own spacing (e.g., DataGrid), adding DialogContent is not necessary.

    <EditDialog>
//...
+ <DialogContent>
+ //...
+ </DialogContent>
// ...
</EditDialog>

✅ Import Tooltip from @comet/admin package

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/tooltip-1-update-import.ts
- import { Tooltip } from "@mui/material";
+ import { Tooltip } from "@comet/admin";

✅ Remove trigger prop from Tooltip

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/tooltip-2-remove-trigger-prop.ts

The trigger prop has been removed. The combined hover/focus trigger is now the only supported behavior.

Example:

<Tooltip
- trigger="hover"
></Tooltip>

Site

✅ Remove graphQLFetch from sitePreviewRoute calls

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/remove-graphql-fetch-from-site-preview-route.ts
site/src/app/site-preview/route.ts
-    return sitePreviewRoute(request, createGraphQLFetch());
+ return sitePreviewRoute(request);

Remove x-relative-dam-urls header from graphQLClient

site/src/util/graphQLClient.ts
// ...
return createGraphQLFetchLibrary(
createFetchWithDefaults(fetch, {
// ...
headers: {
- "x-relative-dam-urls": "1",
// ...
},
}),
`${process.env.API_URL_INTERNAL}/graphql`,
);

ESLint

✅ Upgrade ESLint from v8 to v9 with ESM

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/eslint-dev-dependencies.ts

Update ESLint to v9

package.json

- "eslint": "^8.0.0",
+ "eslint": "^9.0.0",

An ESM compatible ESLint config is required. Delete the related .eslintrc.json and move the configured rules to the new ESLint flat configuration eslint.config.mjs.

Migration Guide of ESLint 9.0 can be found here: Migration Guide

admin/eslint.config.mjs

import cometConfig from "@comet/eslint-config/react.js";

/** @type {import('eslint')} */
const config = [
{
ignores: ["schema.json", "src/fragmentTypes.json", "dist/**", "src/**/*.generated.ts"],
},
...cometConfig
];

export default config;

api/eslint.config.mjs

import cometConfig from "@comet/eslint-config/react.js";

/** @type {import('eslint')} */
import cometConfig from "@comet/eslint-config/nestjs.js";

/** @type {import('eslint')} */
const config = [
{
ignores: ["src/db/migrations/**", "dist/**", "src/**/*.generated.ts"],
},
...cometConfig,
];

export default config;

site/eslint.config.mjs

import cometConfig from "@comet/eslint-config/react.js";

/** @type {import('eslint')} */
import cometConfig from "@comet/eslint-config/nextjs.js";

/** @type {import('eslint')} */
const config = [
{
ignores: ["**/**/*.generated.ts", "dist/**", "lang/**", "lang-compiled/**", "lang-extracted/**", ".next/**", "public/**"],
},
...cometConfig,
];

export default config;

Custom rules

If you have custom rules in your .eslintrc.json, you need to manually move them to the new ESLint flat configuration eslint.config.mjs.

Upgrade Prettier from v2 to v3

Handled by @comet/upgrade
Handled by following upgrade script
npx @comet/upgrade v8/prettier-dev-dependencies.ts
-        "prettier": "^2.8.1",
+ "prettier": "^3.4.2",

Remove React barrel imports

Importing React is no longer necessary due to the new JSX transform, which automatically imports the necessary react/jsx-runtime functions. Use individual named imports instead, e.g, import { useState } from "react".

It is recommended to perform the following steps separately in the admin/ and site/ directories:

  1. Replace import * as React from "react"; with import React from "react"; in your codebase (This step is optional but improves the results of the codemod).

  2. Run the codemod to update React imports (option --force is required to because of changes of step one above):

    npx react-codemod update-react-imports --force
  3. Run ESLint with the --fix option to automatically fix issues:

    npm run lint:eslint --fix

These steps will help automate the process of updating React imports and fixing linting issues, making the migration smoother. The codemod does not handle all cases, so manual adjustments may still be necessary.

✅ Consistent type imports

Handled by @comet/upgrade

The upgrade script runs eslint with the --fix option. That will automatically update the imports.

What is the change?

To improve code consistency and readability, we now enforce the ESLint rule @typescript-eslint/consistent-type-imports with the following configuration:

"@typescript-eslint/consistent-type-imports": [
"error",
{
"prefer": "type-imports",
"disallowTypeAnnotations": false,
"fixStyle": "inline-type-imports"
}
]

Why this change?

This rule ensures that TypeScript type-only imports are explicitly marked with import type, leading to multiple benefits:

  • Improved Code Clarity It is immediately clear that the imported symbol is used only for TypeScript type checking and not at runtime. Avoids confusion between runtime imports and purely static type definitions.
  • Performance & Tree-Shaking TypeScript can optimize build performance since it knows which imports are needed only at compile time. Some bundlers can more effectively remove unused type imports, reducing bundle size.
  • Reduced Circular Dependency Issues Circular dependencies can cause hard-to-debug issues in TypeScript projects. Using import type ensures that types do not introduce unintended runtime dependencies.