---
updatedAt: 2026-03-24T07:00:36.000Z
agentTools:
  projectIndex: https://docs.makaira.io/llms.txt
---

# Storefront Live Preview

# Upgrade Guide for Storefront Live Preview

To use the preview feature of the Page Editor, you have to make some adjustments to your Storefront. If you use the latest version of the Makaira Storefront Starterkit, the preview will work out of the box. The Starterkit contains all the changes described on this page, so you can also look there for a working example.

## Required code changes

> 📘 tl;dr - [Patch with all necessary code changes](https://github.com/MakairaIO/storefront-starter-kit/commit/afd7cd7b81bff8f7c97c9e8afc2178ea87e10b6d.patch)
>
> If you just want to see the code changes at a glance, just checkout the [commit](https://github.com/MakairaIO/storefront-starter-kit/commit/afd7cd7b81bff8f7c97c9e8afc2178ea87e10b6d) - there you can find all necessary changes that need to be applied to your code. The easiest way is to merge the stable branch of <https://github.com/MakairaIO/storefront-starter-kit>
>
> If some changes are unclear you can find the explanations below.

## Deprecation of the main section

The Makaira page structure no longer contains a main section. From now on, all pages consist only of a **top** and a **bottom** section. This was already the case for listing pages but now applies to all page types, especially landing pages.

To adopt these breaking changes, you have to make some edits to `frontend/LandingPage/index.js`\
At first, you have to adopt the check in render function to match the new structure

```javascript frontend/LandingPage/index.js
const config = pageData.data.config || {}

if (!config.bottom && !config.top) return null
```

After that, you have to render two content elements components from now on, and you can also already include the `<ProductList />` component. By doing this, your Storefront is able to display product grids, which will be possible in a future Makaira version.

```javascript
<ContentElements elements={config.top?.elements} />
<ProductList />
<ContentElements elements={config.bottom?.elements} />
```

## Introduction of the new preview

### How does it work?

The Page Editor will load your Storefront at the URL `https://YOUR_DOMAIN.COM/preview` inside an iFrame. After successful load, the Makaira Backend will use the [Window.postMessage()](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage) function to communicate with the Storefront. Initially, a handshake is made where the Makaira Admin UI asks the Storefront to report its current version, so that we know which functionality your Storefront currently supports. After that, the Makaira Admin UI will send the new page data via the postMessage function to the Storefront when changes are made in the edit screen.

> 📘 tl;dr The complete new preview\.js file can be found [here](https://github.com/MakairaIO/storefront-starter-kit/blob/afd7cd7b81bff8f7c97c9e8afc2178ea87e10b6d/pages/frontend/preview.js)
>
> This is also included in the patch file mentioned in the beginning of this guide.

### 1. New route "/preview"

Inside the folder `pages/frontend` create a new file `preview.js`. The content of this file should  match the content of your main Storefront entry point. The entry point is normally located inside `pages/frontend/entry.js`. The only difference for the preview route is, that this route won't fetch the page data directly from the API, but receives the data by listening for messages.

You also have to register the new route in the `server/index.js`. You can do this by adding the following code

```javascript server/index.js
/**
* Route handler for preview endpoint.
*/
server.get('/preview', (req, res) => {
  app.render(req, res, '/frontend/preview', {
  	...req.params,
  })
})
```

> 🚧
>
> The preview route must be placed above the default route `server.get('*', (req, res) =>`

### 2. Set the initial state of the preview route

In the next steps, we will assume, that you're using a class component for the preview page.

The initial state should contain a page type of "loading" so that we can display a loading screen until the data is received from the Page Editor. Also, the state option `isPreview` will be important later on, so that we can correctly update the state.

```javascript pages/frontend/preview.js
constructor(props) {
  super(props)

  this.state = {
    pageData: {
      type: 'loading',
    },
    isPreview: true,
  }
}
```

### 3. Listening for messages inside the preview route

To communicate with the Makaira Backend, the preview route needs an event handler for the **message** event.

```javascript pages/frontend/preview.js
componentDidMount() {
	window.addEventListener('message', this.updateStateForPreview)
}

componentWillUnmount() {
	window.removeEventListener('message', this.updateStateForPreview)
}
```

Next up, add the event handler function itself. It does three things:

1. **Security check** — Only accept messages that originate from the Makaira Backend.
2. **Version handshake** — When the action `reportVersion` is received, respond with `responseVersion` and the version `"1.0"` (this enables all current preview features).
3. **State update** — When the action `update` is received, update the component state with the new page data from the Page Editor.

```javascript
updateStateForPreview = (event) => {
  const { source, payload, action } = event.data

  // Check if we get the data from makaira backend
  if (event.origin !== process.env.MAKAIRA_API_URL) return

  // Check if it is also sent by the makaira backend
  if (source !== 'makaira-bridge') return

  // The makaira backend wants to know which version of the page editor preview
  // this storefront supports. We answer here with the set current version.
  if (action === 'reportVersion') {
    event.source.postMessage(
      {
        source: 'makaira-bridge',
        action: 'responseVersion',
        version: '1.0',
      },
      event.origin
    )
  // Update the state which will update the GlobalDataProvider when we receive
  // new page data from the makaira backend.
  } else if (action === 'update') {
  	this.setState({ pageData: payload.data, isPreview: true })
  }
}
```

### 4. Update the render function of the preview route

The render method only differs at three points from the render method of your `entry.js`.
First, load `pageData` from the component state instead of from props.

```javascript pages/frontend/preview.js
const { pageData } = this.state
```

Then add a check for the loading state. Destructure `type` from `pageData` and return early if the preview data hasn't been received yet.

```javascript pages/frontend/preview.js
if (type === 'loading') {
	return (
    	<Head>
      		<meta name="robots" content="noindex" />
    	</Head>
    )
}
```

This returns a `noindex` meta tag so that the preview route won't be indexed by search engines. The `Head` tag is imported with `import Head from 'next/head'`.

The last change in the render function is to pass the component state (instead of props) to the GlobalDataProvider.

```javascript pages/frontend/preview.js
<GlobalDataProvider {...this.state} params={{}}>
```

### 5. Update the GlobalDataProvider

One small change is also required in the GlobalDataProvider. In preview mode, incoming props need to be copied directly into the component state so that every update from the Page Editor is immediately reflected.

```javascript utils/core/GlobalDataProvider/index.js
static getDerivedStateFromProps(props, state) {
    if (state.isPreview) return props

	…
}
```

### 6. Adding a placeholder for not implemented components

Sometimes when creating a new page in the Page Editor, not all used components are already implemented in the Storefront. To directly see this in the preview, you can add a fallback component, which is displayed when no mapping for the component name could be found.

To add this placeholder, simply add a fallback to the component mapping inside `patterns/core/ContentElements/index.js`

```javascript
const Component = components[entry.component] ?? NoComponent
```

and add the name of the component to the properties of the component.

```javascript patterns/core/ContentElements/index.js
<Component {...entry.properties.content} name={entry.component} />
```

You can find an example implementation of NoComponent in the [Storefront Starterkit Repository](https://github.com/MakairaIO/storefront-starter-kit/tree/stable/patterns/core/NoComponent).

***

# Preview validation

To validate that all your changes work, you can start the Storefront locally and set the domain of the preview to `localhost:PORT`. Then open the developer tools of your browser. If everything works correctly, you'll find two log lines in the console.

<Image align="center" className="border" border={true} src="https://files.readme.io/f00558f-9.png" />

<br />

# Enhancing Component Selection in Page Editor

## Problem Statement

Some pages in the page editor contain a large number of components, making it difficult for editors to identify and edit the specific component they need. To enhance the user experience, we introduce a synchronization mechanism between the component list and the preview.

## Implementation Details

### Selection Event from Admin UI

When a user selects a component to edit, the admin UI will send a message to the storefront using `Window.postMessage()`. The message will include:

* `action`: `selected-element`
* `payload`: The ID of the selected component

### Handling Selection in Storefront

The storefront will use the received ID to visually highlight the selected component. This can be achieved by:

1. Scrolling to the selected component.
2. Adding a border to make it stand out.

For reference, check out the implementation in the following commit:\
[storefront-starter-kit commit 7d88d634cb507415286fa898450b23f9276eae4f](https://github.com/MakairaIO/storefront-starter-kit/commit/7d88d634cb507415286fa898450b23f9276eae4f).

This approach ensures that editors can efficiently locate and modify components, improving the overall usability of the page editor.