> ## Documentation Index
> Fetch the complete documentation index at: https://developers.hubspot.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

---
id: f02ad18d-e5dc-46c1-906c-8ecd756ee030
---

# Local development

> Learn how to run a local development server for building CMS React projects.

`@hubspot/cms-dev-server` is an [NPM package](https://www.npmjs.com/package/@hubspot/cms-dev-server) that facilitates starting an Express + Vite development server, enabling an auto-reloading local development workflow that is nearly identical to your deployed components. The `cms-dev-server` also enables rendering local versions of your components on live CMS pages to help with development.

Below, learn how to install and use the CMS local development server, including how to proxy pages, incorporate Storybook, generate field types, and use secrets during local development.

## Installation

You can install `@hubspot/cms-dev-server` in several ways:

* Install it globally: `npm install -g @hubspot/cms-dev-server`.
* Add it as a dependency in the project's `package.json` file (example below), then run `npm install`.

```json theme={null}
{
  "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": [],
  "engines": {
    "node": ">=20.0.0"
  },
  "dependencies": {
    "@hubspot/cms-components": "latest",
    "react": "^18"
  },
  "devDependencies": {
    "@hubspot/cms-dev-server": "latest"
  }
}
```

## Basic usage

If you've installed the package globally, you can start the local development server by running  `hs-cms-dev-server /path/`. Alternatively, use `npx`, or add a `start` script to your `package.json` to run with `npm`.

```shell theme={null}
npx hs-cms-dev-server .

# Run using `start` script in package.json
npm run start

# Run directly if only installed locally
./node_modules/.bin/hs-cms-dev-server .
```

The server will run on port `3000`, and you can access the main local development dashboard by visiting [http://localhost:3000](http://localhost:3000). This dashboard will provide details and local preview links for the project's CMS assets.

You can also use the `hslocal.net:3000` and `localhost:3000` addresses to preview pages with proxying. For example, a CMS page `https://cmssite.com/page` with React components would be accessible by visiting one of:

* `http://cmssite.com.hslocal.net:3000/page`
* `http://cmssite.com.localhost:3000/page`

Alternatively, visit `http://hslocal.net:3000/proxy` and paste in the URL of the page you want to proxy.

Similar to how previewing a page from the page editor works, you can force the page to render with the context of a known contact by passing an `email` parameter. For example `http://cmssite.com.hslocal.net:3000/page?email=bh@hubspot.com` will cause the contact object to be populated based on the email parameter value.

You may also start the dev server with the `--ssl` option, which enables:

* `https://cmssite.com.hslocal.net:3000/page`
* `https://cmssite.com.localhost:3000/page`

## Routes

When the development server starts, it will look at the `components/modules` directory, then will dynamically create routes based on the modules that it finds. There are two routes for previewing your modules: `/module/moduleName` and `/preview/module/moduleName`.

### /module/moduleName

`http/module/moduleName`

The `/module/moduleName` route is rendered entirely locally without connecting with the HubSpot backend. This enables you to work offline and unauthenticated at this route.

Field values that are used are derived entirely from the Field default values and from parameter-level overrides. Parameter-level overrides can be passed via the `fields` parameter, which expects stringified JSON of `fieldValues` that matches the passed `fieldValues` prop (matching the fields definition structure).

Using this route includes some caveats for querying data:

* GraphQL data in this context is fetched from your local machine using your local access token to authorize the collector service requests. These queries are cached, but you can bust the cache with the `?hsLocalQueryKey` query parameter.
* `hublDataTemplate` is not supported at this route.
* `Icon`, `CTA`, and other `@hubspot/cms-component` [field helpers](/cms/reference/react/cms-components-library) are not supported at this route.

### /preview/module/moduleName

The `/preview/module/moduleName` route interacts with the HubSpot backend and behaves similarly to viewing a module in the design manager previewer.

Field values that are used rely on defaults, as there is no module instance to pull from. There is no `fields` parameter available for overrides.

When querying data on this route:

* GraphQL data is derived on the backend, and there is no query from the local server to the GraphQL service.
* `hublDataTemplate` is supported.
* `Icon`, `CTA`, and other `@hubspot/cms-component` field helpers are supported.

## Proxying private pages

With the local development server running, you can preview your local changes on live HubSpot pages, including [private pages](/cms/start-building/features/memberships/overview) that require passwords or login to access, as well as page previews.

### Proxied membership pages

To view your local changes on a proxied membership page, first visit the page and log in with the configured method. Once authenticated, add `hslocal.net:3000` or `localhost:3000` to the root domain, just as you would with proxying a public page.

To view the page as a specific contact, you can add an `email` query parameter to the URL, followed by the contact's email address, as shown below:

`https://mydomain.hslocal.net:3000/private-page-path?email=hi@hubspot.com`

That email will be used for the [`request_contact`](/cms/reference/hubl/variables-macros-syntax) HubL variable, and can be passed to React using `hublDataTemplate`.

### Proxied preview

To view your local changes on a proxied version of a page preview, first navigate to the page editor in HubSpot, then click **Preview** in the upper right, then **Open in a new tab**.

<Frame>
  <img src="https://www.hubspot.com/hubfs/Knowledge_Base_2023_2024/hubspot-editor-open-page-preview.png" alt="page-editor-open-preview" />
</Frame>

With the page preview open in a new tab, add `hslocal.net:3000` or `localhost:3000` to the root domain. To view the page as a specific contact, you can add an `email` query parameter to the URL, followed by the contact's email address, as shown below:

`https://mydomain.hslocal.net:3000/private-page-path?hs_preview=[preview_key]&email=hi@hubspot.com`

## Storybook

The local development server includes a [Storybook](https://storybook.js.org/) integration which allows you to start a Storybook instance alongside the development server. Include a `--storybook` option when running the command to start the local development server.

Once started, you can add `.stories.jsx` files alongside your components to build stories for testing or development. At the root `http://hslocal.net:3000` page, a link will be included to the Storybook UI for your project. To make building stories for HubSpot modules easier, cms-dev-server provides helpers to auto-generate `argTypes` based on module fields. See the [GraphQL and Storybook example project](https://github.com/HubSpot/cms-react/tree/main/examples/graphql-storybook/gql-storybook-project/gql-storybook-app) for usage of `moduleStory()`.

Storybook is built with client components in mind, so components that cross island boundaries can have unexpected lifecycle behavior when rendered in a story. Because server-only components never make it to the browser, they cannot be hot reloaded, so a full re-render is necessary to update the server response. To fully emulate hybrid rendering in Storybook at the cost of hot module reloading, you can use `moduleStoryWithIsland()` in your story in place of `moduleStory()`.

## Fields type generation

If you're using TypeScript in your project, you can use the `--generateFieldsTypes` argument when starting the development server. This command will watch for changes to the `fields` object that is exported from module files and create a `.types.ts` file inside of the module directory. You can then import this module directly in your module component and use it in the generate `ModuleProps<T>` type.

For example, suppose you have the following `fields.jsx` file:

```jsx theme={null}
export const fields = (
  <ModuleFields>
    <ChoiceField
      label="Choice Test"
      name="choice"
      display="select"
      choices={[
        ["choice1", "One"],
        ["choice2", "Two"],
      ]}
      default="choice1"
    />
    <NumberField label="Display on each blog post" name="numberField" />
    <FieldGroup name="defaultGroup" label="Default text" locked>
      <TextField label="Text Field One" name="textFieldOne" default="Text Field" />
      <TextField label="Text Field Two" name="textFieldTwo" />
      <NumberField label="Number Field" name="numberField" />
    </FieldGroup>
  </ModuleFields>
);
```

Running `hs-cms-dev-server /path/to/project --generateFieldsTypes` will generate a `modules/MyModule/fields.types.ts` file with a default exported type `MyModuleFieldsType`. The above `fields.tsx` will generate the following file:

```jsx theme={null}
// THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
// Removing the above comment will disable type generation for this module
// This file was created by @hubspot/cms-dev-server, for more information see https://github.hubspot.com/cms-react/reference/js-modules.html#module-fields
import { type DefaultValues, type ChoiceFieldType, type NumberFieldType, type TextFieldType, type GroupFieldType, type Override } from "@hubspot/cms-components/fields";
type MyModuleFieldsType = DefaultValues<{
    choice: Required<ChoiceFieldType>;
    numberField: NumberFieldType;
    defaultGroup: Override<GroupFieldType, {
        children: {
            textFieldOne: Required<TextFieldType>;
            textFieldTwo: TextFieldType;
            numberField: NumberFieldType;
        };
    }>;
}>;
export default MyModuleFieldsType;
```

Then, you can import and use the type in your component as follows:

```jsx theme={null}
import { ModuleProps } from '@hubspot/cms-components';
import MyModule from './fields.types';

export const Component = ({
  fieldValues,
  hublParameters = {},
}: ModuleProps<MyModule>) => {
  const number = fieldValues.numberField;
  //   ^?const number: number | number[] | null | undefined
  //   Note that can be undefined because no default set

  const choice = fieldValues.choice;
  //   ^?const choice: string | number | (string | number)[]

  const text = fieldValues.defaultGroup.textFieldOne
  //   ^?const text: string | null
}
```

Note that the generated `fields.types.ts` file will be overwritten every time there is an update made to the fields object. To disable this behavior, remove the `THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY` comment at the top of the file.

## Secrets in local development

To make secrets available for local development via `@hubspot/cms-dev-server`, create a `.env` file to define secret values for local use only. Keys in this file need to be prefixed with `HS_` for the development server to recognize it as a secret.

```shell theme={null}
HS_TEST_SECRET=localsecretvalue
```

This secret will be accessible locally without the `HS_` prefix you included in your `.env` file. In the example above, the secret can be accessed with `getSecret('TEST_SECRET')`.
