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

# Dynamic rendering with InstantSearch.js

> InstantSearch.js doesn't support server-side rendering, but you can get the same benefits by implementing dynamic rendering.

<Warning>
  Dynamic rendering is now considered a workaround instead of a recommended approach and Rendertron, the tool mentioned in this guide, is deprecated. You can use server-side rendering with [React InstantSearch](/doc/guides/building-search-ui/going-further/server-side-rendering/react) and [Vue InstantSearch](/doc/guides/building-search-ui/going-further/server-side-rendering/vue).
</Warning>

InstantSearch.js doesn't support server-side rendering (SSR). However, it's possible to get some of the SSR benefits using another technique: [dynamic rendering](https://developers.google.com/search/docs/guides/dynamic-rendering).

## Dynamic rendering

[Dynamic rendering](https://developers.google.com/search/docs/guides/dynamic-rendering) lets you switch between client-side rendered and pre-rendered content for specific user agents, typically search engine user agents.
This means that whenever your server receives a request, it first looks at the user agent to determine whether it's coming from a search engine or a regular user.

* If the request is coming from a search engine, the response is proxied through a headless browser that pre-renders it.
* If the request is coming from a regular user, the response is rendered client-side.

### Build an InstantSearch app

The easiest way to setup a basic InstantSearch app is using [`create-instantsearch-app`](https://github.com/algolia/create-instantsearch-app/).
Install [create-instantsearch-app](https://github.com/algolia/create-instantsearch-app/) and create your app.

```bash Shell theme={"system"}
npm install -g create-instantsearch-app@latest
create-instantsearch-app my-app
```

<Note>
  Feel free to pick any InstantSearch flavor your like. Pick the default values for the rest of the options.
</Note>

Run the build command and confirm it produced a `dist/` folder.

```bash Shell theme={"system"}
cd my-app
npm run build
```

Add a basic [Express.js](https://expressjs.com/) server that will serve static files.

```bash Shell theme={"system"}
npm install --save express
touch index.js
```

```js JavaScript theme={"system"}
// index.js
const express = require('express');
const app = express();

app.use(express.static('dist'));

const port = process.env.PORT || 3000;
app.listen(port, err => {
  if (err) {
    console.error('failed to start server.');
    return;
  }
  console.log(`server started at http://localhost:${port}/`);
});
```

Now run the server and visit [http://localhost:3000](http://localhost:3000) to make sure everything is working.

```bash Shell theme={"system"}
node index.js
```

### Set up and configure a dynamic renderer

Google recommends using either [rendertron](https://github.com/GoogleChrome/rendertron) (free, self-hosted) or [prerender.io](https://prerender.io/) (fully hosted).
This example uses [rendertron](https://github.com/GoogleChrome/rendertron).

```bash Shell theme={"system"}
npm install -g rendertron
PORT=3001 rendertron &
```

The pre-renderer is now listening on port `3001` and ready to render JavaScript generated content into HTML.

### Proxy your requests through Rendertron

You need to send every request with the `Content-Type: text/html` header and the relevant user agent to the URL of Rendertron: [`http://localhost:3001/render`](http://localhost:3001/render).

Depending on your stack, there are multiple ways you can achieve this kind of proxying, but if you're using an [Express.js](https://expressjs.com/) server, [rendertron-middleware](https://github.com/GoogleChrome/rendertron/blob/master/middleware/) can handle this proxying for you.

```bash Shell theme={"system"}
npm install --save rendertron-middleware
```

```js JavaScript theme={"system"}
const rendertronMiddleware = require('rendertron-middleware');

/* ... */

app.use(
  rendertronMiddleware.makeMiddleware({
    proxyUrl: `http://localhost:3001/render/`, // rendertron address with a /render/ suffix
    timeout: 60 * 1000,
    userAgentPattern: /googlebot|bingbot/i // by default
  })
);

app.use(express.static('dist'));

/* ... */
```

<Note>
  Feel free to adjust the `userAgentPattern` or use the [default values](https://github.com/GoogleChrome/rendertron/blob/4f396d4d522cd39ceaa0b33b203afd81371e28f5/middleware/src/middleware.ts#L25-L40).
</Note>

Close and restart your server, then run this curl command to confirm pre-rendering works.

```bash Shell theme={"system"}
curl --user-agent "googlebot" http://localhost:3000/
```
