# 04 Server-side rendering Up until now, we have focused on Lustre's ability as a framework for building Single Page Applications (SPAs). While Lustre's development and feature set is primarily focused on SPA development, that doesn't mean it can't be used on the backend as well! In this guide we'll set up a small [mist](https://hexdocs.pm/mist/) server that renders some static HTML using Lustre. ## Setting up the project We'll start by adding the dependencies we need and scaffolding the HTTP server. Besides Lustre and Mist, we also need `gleam_erlang` (to keep our application alive) and `gleam_http` (for types and functions to work with HTTP requests and responses): ```sh gleam new app && cd app && gleam add gleam_erlang gleam_http lustre mist ``` Besides imports for `mist` and `gleam_http` modules, we also need to import some modules to render HTML with Lustre. Importantly, we _don't_ need anything from the main `lustre` module: we're not building an application with a runtime! ```gleam import gleam/bytes_builder import gleam/erlang/process import gleam/http/request.{type Request} import gleam/http/response.{type Response} import lustre/element import lustre/element/html.{html} import mist.{type Connection, type ResponseData} ``` We'll modify Mist's example and write a simple request handler that responds to requests to `/greet/:name` with a greeting message: ```gleam pub fn main() { let empty_body = mist.Bytes(bytes_builder.new()) let not_found = response.set_body(response.new(404), empty_body) let assert Ok(_) = fn(req: Request(Connection)) -> Response(ResponseData) { case request.path_segments(req) { ["greet", name] -> greet(name) _ -> not_found } } |> mist.new |> mist.port(3000) |> mist.start_http process.sleep_forever() } ``` Let's take a peek inside that `greet` function: ```gleam fn greet(name: String) -> Response(ResponseData) { let res = response.new(200) let html = html([], [ html.head([], [html.title([], "Greetings!")]), html.body([], [ html.h1([], [html.text("Hey there, " <> name <> "!")]) ]) ]) response.set_body(res, html |> element.to_document_string |> bytes_builder.from_string |> mist.Bytes ) } ``` The `lustre/element` module has functions for rendering Lustre elements to a string (or string builder); the `to_document_string` function helpfully prepends the `` declaration to the output. It's important to realise that `element.to_string` and `element.to_document_string` can render _any_ Lustre element! This means you could take the `view` function from your client-side SPA and render it server-side, too. ## Hydration If we know we can render our apps server-side, the next logical question is how do we handle _hydration_? Hydration is the process of taking the static HTML generated by the server and turning it into a fully interactive client application, ideally doing as little work as possible. Most frameworks today support hydration or some equivalent, for example by serialising the state of each component into the HTML and then picking up where the server left off. Lustre doesn't have a built-in hydration mechanism, but because of the way it works, it's easy to implement one yourself! We've said many times now that in Lustre, your `view` is just a [pure function](https://github.com/lustre-labs/lustre/blob/main/pages/hints/pure-functions.md) of your model. We should produce the same HTML every time we call `view` with the same model, no matter how many times we call it. Let's use that to our advantage! We know our app's `init` function is responsible for producing the initial model, so all we need is a way to make sure the initial model on the client is the same as what the server used to render the page. ```gleam pub fn view(model: Int) -> Element(Msg) { let count = int.to_string(model) html.div([], [ html.button([event.on_click(Decr)], [html.text("-")]), html.button([event.on_click(Incr)], [html.text("+")]), html.p([], [html.text("Count: " <> count)]) ]) } ``` We've seen the counter example a thousand times over now, but it's a good example to show off how simple hydration can be. The `view` function produces some HTML with events attached, but we already know Lustre can render _any_ element to a string so that shouldn't be a problem. Let's imagine our HTTP server responds with the following HTML: ```gleam import app/counter import gleam/bytes_builder import gleam/http/response.{type Response} import gleam/json import lustre/attribute import lustre/element.{type Element} import lustre/element/html.{html} import mist.{type ResponseData} fn app() -> Response(ResponseData) { let res = response.new(200) let model = 5 let html = html([], [ html.head([], [ html.script([attribute.type_("module"), attribute.src("...")], ""), html.script([attribute.type_("application/json"), attribute.id("model")], json.int(model) |> json.to_string ) ]), html.body([], [ html.div([attribute.id("app")], [ counter.view(model) ]) ]) ]) response.set_body(res, html |> element.to_document_string |> bytes_builder.from_string |> mist.Bytes ) } ``` We've rendered the shell of our application, as well as the counter using `5` as the initial model. Importantly, we've included a `