• Cheat sheets
  • Documentation
  • API reference
  • Product updates
  • Sign in
Kontent.ai Learn
  • Try Kontent.ai
  • Plan
  • Set up
  • Model
  • Develop
  • Create
Copyright © 2025 Kontent.ai. All rights reserved.
  • Web
  • Privacy policy
  • Cookies policy
  • Consent settings
  • Security
  • GDPR
  • Overview
  • Manage API keys
  • Hello World
  • Hello Web Spotlight
  • Try sample apps
  • Build apps
  • Decide navigation and URLs
  • Environments
    • Overview
    • Migrate from other systems
    • Automate migrations with CLI
    • Import content via API
  • Get Developer Certification

Map content to new structure

Jan Cerman
14 minutes
Download PDF
  • TypeScript
0% complete
Define how to map your exported content to your new content model. To make the mapping easier, use the Kontent.ai Migration toolkit to ensure your content items and assets are correctly structured and referenced in your new project.

Mapping on paper

Before you start writing the mapping logic in code, we recommend you first define the mapping visually. You can do this on digital or analog paper. Visual mapping lets you and other stakeholders see how your existing content fits within the new content model. You’ll see which parts fit and which don’t. If a part doesn’t fit, you can adjust the content model or discard that part if it’s not needed. This lets you find possible gaps in your content model early on.

Example mapping

Check the following diagram to see how an existing structure used for blog posts in one system can be mapped to a content model in Kontent.ai. The Kontent.ai content model in the example is based on the Article content modeling accelerator. Notice that the Blog post’s fields don’t always map one-to-one to the Article’s elements; sometimes, you need to transform the existing content to another format. Text fields can usually be mapped 1:1. For anything more complex, there is often a transformation involved.
Mapping on paper can reveal gaps in your model. In this case, the Article’s Subtitle and Tags elements don’t match any of the original Blog post’s fields. In your case, it might be the other way around—some of the original fields might not match elements in the new model. For example, you might have fields that define visual information that you no longer need.If this happens, you might want to remove these extra elements unless you plan to use them for mapping from other types of content as well.

Mapping in code

Once you’ve verified everything on paper, you can move to the real thing. To simplify the mapping in code, use the Kontent.ai Migration toolkit. The toolkit handles requests to Management API for you and makes the content migration easier. For example, the toolkit checks for existing assets and content items to ensure they’re referenced correctly.

Install the Migration toolkit

To get started, create a Node.js app and install the Migration toolkit by running the following command in the terminal.
  • Shell

Migration toolkit 101

To migrate your exported content to a Kontent.ai project with the Migration toolkit, you need to:
  1. Map your textual and structured content to the MigrationItem objects.
  2. Map your binary files to the MigrationAsset objects.
  3. Import the mapped content from the MigrationItem and MigrationAsset objects to your Kontent.ai project.
Let’s now see how to map content items and assets.

Map exported content to content items

Kontent.ai stores content in content items. Depending on the number of languages in your project, each content item has one or more content item variants. The Migration toolkit’s MigrationItem represents a single localized variant of a content item. This includes the variant’s metadata and elements. The specific elements depend on the content item’s content type. With each MigrationItem, you can choose to specify content for the content item variant’s latest version, published version, or both.
The following example shows how you can map content to a MigrationItem. We recommend using strongly typed models for the MigrationItem objects.

Parse rich text content

Plan for any necessary parsing of your existing rich text content to map it to the HTML5 rich text format supported by Kontent.ai’s Management API. Remember that the rich text editor in Kontent.ai doesn’t support custom visual formatting, such as setting text color or font size, for a reason. If your existing rich text content contains visual formatting, you need to decide whether to remove it or transform it into structured content. We recommend removing such custom formatting in most cases. Instead, let your web app or mobile app define how the content should be displayed. It will also mean your content creators can focus on the content instead of the formatting or visual options.

Map binary files to assets

The Migration toolkit’s MigrationAsset represents a single asset. You can reference assets by codenames in multiple MigrationItem objects. Every asset can specify localized asset descriptions for use as alt text.

Putting it together

Now that you know how to map your content and binary files to the migration objects, create mapping logic for your existing types of content.Once you have all the content mapped, you can start importing it to your project.
Sign in with your Kontent.ai credentials or sign up for free to unlock the full lesson, track your progress, and access exclusive expert insights and tips!
Sign in
  • TypeScript
  • TypeScript
  • TypeScript
  • TypeScript
  • Before you begin
  • Prepare your existing content
  • Prepare your Kontent.ai project
  • Map content to new structure
  • Migrate your content
# Add the Migration toolkit to your app
npm i @kontent-ai/migration-toolkit --save-dev
import {
  MigrationAsset,
  MigrationItem,
  importAsync,
} from "@kontent-ai/migration-toolkit";

// 1. Map the exported content to MigrationItems
const migrationItems: MigrationItem[] = [];
// 2. Map exported files to MigrationAssets
const migrationAssets: MigrationAsset[] = [];
// 3. Import the mapped data into a Kontent.ai project
await importAsync({
  data: {
    assets: migrationAssets,
    items: migrationItems,
  },
  environmentId: "KONTENT_AI_ENVIRONMENT_ID",
  apiKey: "MANAGEMENT_API_KEY",
});
import { MigrationItem } from "@kontent-ai/migration-toolkit";

// MigrationItem defines a localized variant of a content item
const migrationItem: MigrationItem = {
  // Variant's metadata
  system: {
    // Name the content item. Item name is shared for all variants.
    name: "My content item",
    // Generate a unique content item codename
    codename: "my_content_item",
    // Assign the item to a collection
    collection: { codename: "default" },
    // Specify the variant's language
    language: { codename: "en_us" },
    // Specify the content type
    type: { codename: "article" },
    // Assign the item to a workflow
    workflow: { codename: "default" },
  },
  versions: [
    {
      // Put the variant in a specific workflow step
      workflow_step: { codename: "draft" },
      elements: {
        // Variant's content
        // For each element specified by the item's content type, add properties named using element codenames.
        // Example: element_codename: elementsBuilder.textElement({ value: 'plaintext' }),
      },
    },
  ],
};
import {
  MigrationElementModels,
  MigrationItem,
  MigrationItemSystem,
  elementsBuilder,
} from "@kontent-ai/migration-toolkit";

// We recommend you define the structure of your Kontent.ai content type
type LanguageCodenames = "default" | "en";
type CollectionCodenames = "default" | "global";
type WorkflowCodenames = "default" | "custom";
type WorkflowStepCodenames = "published" | "archived" | "draft";
type ContentTypeCodenames = "movie" | "actor";
type System<Codename extends ContentTypeCodenames> = MigrationItemSystem<
  Codename,
  LanguageCodenames,
  CollectionCodenames,
  WorkflowCodenames
>;

type MovieItem = MigrationItem<
  // Defines the elements in the 'Movie' content type defined in Kontent.ai
  {
    title: MigrationElementModels.TextElement;
    plot: MigrationElementModels.RichTextElement;
    length: MigrationElementModels.NumberElement;
    category: MigrationElementModels.MultipleChoiceElement;
    poster: MigrationElementModels.AssetElement;
    stars: MigrationElementModels.LinkedItemsElement;
    seoname: MigrationElementModels.UrlSlugElement;
    released: MigrationElementModels.DateTimeElement;
    releasecategory: MigrationElementModels.TaxonomyElement;
  },
  System<"movie">,
  WorkflowStepCodenames
>;

const movie: MovieItem = {
  system: {
    name: "Warrior",
    // Ensure a unique codename. Check https://kontent.ai/learn/rules-for-codenames
    codename: "warrior",
    collection: { codename: "default" },
    language: { codename: "default" },
    type: { codename: "movie" },
    workflow: { codename: "default" },
  },
  // Specify up to 2 versions of the variant - latest and published.
  // The latest version can be in any workflow step.
  versions: [
    {
      workflow_step: {
        // You can publish the variant during the import, or use any other workflow step
        codename: "published",
      },
      elements: {
        title: elementsBuilder.textElement({ value: "Warrior" }),
        length: elementsBuilder.numberElement({ value: 140 }),
        category: elementsBuilder.multipleChoiceElement({
          value: [
            {
              codename: "drama",
            },
            {
              codename: "action",
            },
          ],
        }),
        poster: elementsBuilder.assetElement({
          value: [
            {
              codename: "warrior_teaser",
            },
          ],
        }),
        plot: elementsBuilder.richTextElement({
          // Check allowed HTML elements in rich text value at https://kontent.ai/learn/rich-text-in-mapi
          value: `<h1>Warrior</h1><p>...</p>`,
          components: [],
        }),
        releasecategory: elementsBuilder.taxonomyElement({
          value: [
            {
              codename: "global_release",
            },
          ],
        }),
        released: elementsBuilder.dateTimeElement({
          value: "2011-09-09T00:00:00Z",
        }),
        seoname: elementsBuilder.urlSlugElement({
          // The value is empty because it's autogenerated based on a dependent text element
          mode: "autogenerated",
          value: "",
        }),
        stars: elementsBuilder.linkedItemsElement({
          value: [
            {
              codename: "tom_hardy",
            },
          ],
        }),
      },
    },
  ],
};

const migrationItems: MigrationItem[] = [movie];
import { MigrationAsset } from "@kontent-ai/migration-toolkit";
import { readFileSync } from "fs"; // Only if using local data

const coverAsset: MigrationAsset = {
  // You can read the data from anywhere, not just from the filesystem
  binary_data: readFileSync("./movies/posters/warrior.jpg"),
  // Ensure a unique asset codename. Check https://kontent.ai/learn/rules-for-codenames
  // This codename is used to reference the asset in the MigrationItem object
  codename: "warrior_teaser",
  // Name the binary file linked to the asset
  filename: "warrior_teaser.jpg",
  // Name the asset
  title: "Warrior cover",
  // (Optional) Asign the asset to a collection
  collection: {
    codename: "default",
  },
  // (Optional) Specify localized asset descriptions
  descriptions: [
    {
      language: {
        codename: "default",
      },
      description: "Poster for Warrior movie",
    },
  ],
};

const migrationAssets: MigrationAsset[] = [coverAsset];
  • Mapping on paper
  • Mapping in code
  • Map exported content to content items
  • Map binary files to assets
  • Putting it together