Last modified: August 22, 2025
As a HubSpot developer, many pages that you develop will be static HTML, such as blog posts, knowledge base articles, and landing pages. But other times, you’ll need to build interactive page elements, such as image carousels, forms with client validation, or live chat widgets. With traditional HubL modules, adding interactivity often requires rendering these interactive elements client-side using JavaScript. This can negatively impact page performance and SEO. By using islands, you can avoid this issue by taking advantage of both client-side and server-side rendering. An island is a subtree of your interactive module. When serving a page to a visitor, the page is fully rendered server-side, initially rendering the page HTML without interactivity. Then, when interaction is needed, the island’s JavaScript is loaded, run, and attached to the page. This process of hydration takes advantage of client-side rendering to optimize the addition of React components to the server-rendered HTML. Using islands comes with several benefits, including: Below, learn how to implement islands, along with available island props and hydration behavior.

Implementation

  • Import Island from @hubspot/cms-components.
  • Import the module you want to wrap, adding ?island to the end of the import URL.
  • Render the Island component, passing the module name into the module prop.
  • Any serializable props that you want to pass to the module, excluding functions, can be passed as props in the Island component.
import { Island } from '@hubspot/cms-components';
import WeatherForecast from '../../islands/WeatherForecast.tsx?island';
import { ModuleFields, TextField } from '@hubspot/cms-components/fields';

export function Component({ fieldValues }: any) {
  const { headline } = fieldValues;
  return <Island module={WeatherForecast} headline={headline} />;
}

export const fields = (
  <ModuleFields>
    <TextField
      label="Weather Headline"
      name="headline"
      default="Get the latest weather forecast"
    />
  </ModuleFields>
);

export const meta = {
  label: 'Weather Module',
};

Available props

Below are the available Island component props.
ParameterTypeDescription
clientOnlyBooleanWhen set to true, the island won’t be rendered on the server. This can be useful for components that rely on logic or libraries that can only run in the browser. Default is false.
hydrateOn"load" (default) | "visible" | "idle"Specifies the type of type of hydration to use. When rendering a page with islands on the server, the output includes a script to initialize islands on the client. Hydrating means downloading and initializing the island component code, so using these different hydration types strategically to defer some of that work can help boost initial page load performance.
idStringBy default, HubSpot generates a unique ID for each island (e.g., island-1234). Use this prop to specify your own island ID.
moduleReact.ComponentThe component to wrap. The component must include ?island at the end of the import URL.
wrapperTagStringThe HTML tag used as the wrapper. Must be a valid HTML element tag (e.g., "span", "article", or "section"). Default is "div".
wrapperClassNameStringA class name to pass to the wrapper.
wrapperStyleCSSPropertiesInline CSS properties to apply to the wrapper.
wrapperReact.ComponentWraps the React tree of the Island component to provide custom context. This can be useful for integrating with CSS-in-JS libraries, such as styled-components, or other context providers that need to encapsulate the component’s subtree for applying styles or context values. When using this prop, the component import URL must include a ?client suffix to ensure that it’s bundled for the client (similar to importing with the ?island suffix).

Hydration types

By default, the island initialization script will hydrate all islands as soon as possible (i.e., on load). But for more complex pages, you may want to defer hydration of non-critical page elements until the browser has finished all other work, or until the visitor scrolls down to the island. Using the hydrateOn prop, you can specify one of the following hydration behaviors:
  • load (default): hydrates the island on initial page load.
  • idle: defers hydration by using the requestIdleCallback method. This can be useful for lower priority components, allowing client resources to be used first on higher priority items.
  • visible: hydration won’t occur until the element is visible on screen by using the Intersection Observer API. This can be useful for components that aren’t immediately visible to the user on page load (i.e., elements farther down the page). For complex islands, this can provide a significant performance benefit: if the user never scrolls to see the island, it will never be loaded.