Last modified: August 22, 2025
Migrating an existing HubL theme to the projects framework involves restructuring the theme directory, updating file extensions, and adding necessary configuration files. This guide outlines the steps to perform this migration. After following the steps in this guide, you’ll have built and deployed a copy of your existing theme using the projects framework. You can then use the theme in your HubSpot account like any other theme, including swapping your existing pages to the new templates or using them to create new pages.
It’s important to note that you won’t be replacing the existing theme in your account but rather creating a copy of your theme within the projects framework. As a result, there are some manual steps involved for moving existing pages to the new theme.
1

Restructure your theme

Your existing HubL theme needs to be restructured to be compatible with the projects framework, which requires a specific structure. Before migrating, your theme’s file structure may look similar to the following:
theme/
├── assets/
├── modules/
├── partials/
├── styles/
├── templates/
├── fields.json
├── theme.json
To adapt your theme to this structure:
  1. Create a new local directory to serve as the container for the project as a whole. This directory can have any name.
  2. In the new directory, create an hsproject.json configuration file. This file configures the project itself. Paste the following code into the project. You can use any name value you’d like.
    {
      "name": "my-project",
      "srcDir": "src",
      "platformVersion": "2023.2"
    }
    
  3. Create an src directory, which will contain your theme files. This directory can have any name, but will need to match the srcDir value defined in the hsproject.json file.
  4. Move your existing theme directory into the src directory.
  5. Within the theme directory, add a package.json file to include the required dependencies: @hubspot/cms-components and @hubspot/cms-dev-server. You can use the example code below to get started:
    {
      "name": "cms-react-theme",
      "version": "1.0.0",
      "description": "CMS React theme",
      "scripts": {
        "start": "hs-cms-dev-server .",
        "deploy": "hs project upload"
      },
      "type": "module",
      "keywords": [],
      "dependencies": {
        "@hubspot/cms-components": "latest",
        "react": "^18"
      },
      "devDependencies": {
        "@hubspot/cms-dev-server": "latest"
      }
    }
    
  6. Within your the theme directory, add a components directory, then add a modules directory within it (src/theme/components/modules). This directory will contain your theme’s React modules. If your existing HubL modules are contained in a modules directory, you’ll need to rename that directory (e.g., hubl-modules), because modules is reserved for React modules within the projects framework.
After making the above updates, your project structure will resemble the following:
project/
├── package.json          # Project-level dependencies
├── hsproject.json        # Project configuration
├── src/
│   └── theme/
│       ├── assets/       # Static assets like images and fonts
│       ├── components/   # React components and modules
│       │   └── modules/
│       ├── hubl-modules/ # Your existing HubL modules (renamed from 'modules')
│       ├── partials/     # HubL partials
│       ├── styles/       # CSS and other style files
│       ├── templates/    # HubL templates
│       ├── fields.json   # Theme fields definition
│       ├── theme.json    # Theme configuration
│       └── package.json  # Dependencies for React components
Note that the above file structure includes two package.json files:
  • The top-level package.json is optional, but it’s where you can install certain development dependencies that may be helpful during local development (e.g., prettier, eslint).
  • The theme-level package.json is required, as developing React modules requires the @hubspot/cms-components and @hubspot/cms-dev-server dependencies.
2

Add a React module

Currently, a theme must contain at least one React module in order for the build to succeed. If you haven’t built any React modules yet, you can add a placeholder module so that you can upload the theme.In the src/components/modules directory, create a PlaceholderModule directory, then add an index.jsx file containing the following code:
import { ModuleFields, TextField } from '@hubspot/cms-components/fields';

export function Component({ fieldValues }) {
  return (
    <div>
      <h1>{fieldValues.headline}</h1>
    </div>
  );
}

export const fields = (
  <ModuleFields>
    <TextField
      name="headline"
      label="Headline"
      default="Placeholder React module"
    />
  </ModuleFields>
);

export const meta = {
  label: 'Placeholder module',
};

With the new module added, your project structure should now look similar to the following:
project/
├── package.json
├── hsproject.json
├── src/
│   └── theme/
│       ├── assets/
│       ├── components/
│       │   └── modules/
│       │       └── PlaceholderModule/
│       │           └── index.jsx
│       ├── ...
│       └── package.json
3

Update HubL file extensions

In CMS React projects, all HubL files (modules, templates, partials, CSS, JS) must be renamed to include .hubl in the file extension.
  • module.html becomes module.hubl.html
  • template.html becomes template.hubl.html
  • partial.html becomes partial.hubl.html
  • module.css becomes module.hubl.css
  • module.js becomes module.hubl.js
This renaming is crucial for the build process to correctly identify and process HubL files within the project structure. Because you’re renaming these assets, you’ll need to update any references in your code to match the new extensions. For example, if one of the theme’s stylesheets includes another, you would need to update the path to use hubl.css:
  • Before: {% include './elements/buttons.css' %}
  • After: {% include './elements/buttons.hubl.css' %}
Please note:Keep in mind that these renamed files will look and function the same but will exist as new assets in your account. When swapping a page to a new .hubl.html template, existing page content will not be impacted (i.e., live website content will stay the same). However, if you want to update that content in the editor, you’ll need to manually rebuild it by adding the new module to the page and recreating the content.
4

Deploy your project to HubSpot

With these changes in place, you can upload the theme to your account by running hs project upload. This command will build and deploy both your HubL and React assets, so you’ll no longer need to use hs upload. If you would like to watch for changes, you can use the project-specific watch command: hs project watch. However, this is less necessary when working with CMS React because you can view your local changes to both HubL and React assets without requiring a deploy to your account.

Next Steps

Now that you have successfully migrated your HubL theme to projects, you’re ready to start creating React modules. Check out the following resources to help you get starting with CMS React modules: