How to SSR
In the most simple case, server side rendering in Percy
boils down to
rendering your virtual DOM to a String
and responding to a client with
that String
.
use percy_dom::prelude::*; use std::cell::Cell; fn main () { let count_cell = Cell::new(5); let app = html! { <div id="app"> <button onclick=|_ev| { *count+= 1; }> Hello world </button> </div> }; let html_to_serve = app.to_string(); // <div id="app"><button>Hello world</button></div> // .. server string to client (http response) ... }
Hydrating initial state
You'll usually want your views to be rendered based on some application state. So, typically, your server will
- Receive a request from the client
- Set the initial application state based on the request
- Render the application using the initial state
- Reply with the initial HTML and the initial state
- Client takes over rendering, starting from the initial state.
To illustrate we'll take a look at an excerpt from a more realistic server side rendering example.
Afterwards you can check out the full example at examples/isormorphic.
A more realistic server side rendering implementation would look like the following:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" type="text/css" href="/static/app.css"/>
<link rel="shortcut icon" href="#" />
<title>Rust Web App</title>
</head>
<body style='margin: 0; padding: 0; width: 100%; height: 100%;'>
<div id="isomorphic-rust-web-app" style='width: 100%; height: 100%;'>
#HTML_INSERTED_HERE_BY_SERVER#
</div>
<script>
function downloadJson(path, callback) {
fetch(path)
.then(function(response) {
return response.json();
})
.then(function(json) {
callback(json);
});
}
</script>
<script type=module>
let client
let updateScheduled = false
window.GlobalJS = function () {}
// TODO:
// https://rustwasm.github.io/wasm-bindgen/api/web_sys/struct.Window.html#method.request_animation_frame
window.GlobalJS.prototype.update = function () {
if (!updateScheduled) {
requestAnimationFrame(() => {
client.render()
updateScheduled = false
})
}
updateScheduled = true
}
window.global_js = new GlobalJS()
import { Client, default as init } from '/static/isomorphic_client.js';
async function run() {
await init('/static/isomorphic_client_bg.wasm');
client = new Client(window.initialState)
}
run();
</script>
<script>
window.initialState = '#INITIAL_STATE_JSON#'
</script>
</body>
</html>
#![allow(unused)] fn main() { // examples/isormorphic/server/src/rocket_server.rs // Check out the full application in /examples/isormorphic directory {{#include ../../../../examples/isomorphic/server/src/rocket_server.rs}} }
And then the client would use serde
to deserialize the initialState
into a State struct and begin rendering using that State.