inertia_wisp/ssr
Server-side rendering support for Inertia.js applications.
This module provides the public API for adding SSR to your Inertia handlers. It wraps your HTML template function to first attempt server-side rendering via Node.js, falling back to client-side rendering if SSR fails.
Types
Page layout function that receives SSR head elements and body content.
head: List of HTML strings for the<head>section (scripts, styles, meta tags)body: The rendered HTML body content
pub type PageLayout =
fn(List(String), String) -> String
Type alias for pool names - a typed handle for pool lookup.
Create pool names using process.new_name() at application startup.
pub type PoolName =
process.Name(@internal PoolMessage)
Configuration for the SSR pool and rendering behavior.
Fields
module_path: Absolute path to the JavaScript SSR module (usepriv_pathto resolve)name: Pool name for registration (default: uses default name)node_path: Optional custom path to Node.js executable (default: None, uses system PATH)pool_size: Number of persistent Node.js worker processes (default: 4)timeout: Maximum time to wait for SSR rendering (default: 1 second)
pub type SsrConfig {
SsrConfig(
module_path: String,
name: process.Name(@internal PoolMessage),
node_path: option.Option(String),
pool_size: Int,
timeout: duration.Duration,
)
}
Constructors
-
SsrConfig( module_path: String, name: process.Name(@internal PoolMessage), node_path: option.Option(String), pool_size: Int, timeout: duration.Duration, )
Values
pub fn default_config() -> SsrConfig
Create a default SSR configuration.
Uses a default pool name, “priv/ssr/ssr.js” as module path, 4 workers,
and 1s timeout. For production releases, use priv_path() to resolve
the module path correctly.
Example
let config = SsrConfig(
..ssr.default_config(),
module_path: ssr.priv_path("my_app", "ssr/ssr.js"),
)
Note: This function creates a new pool name each time it’s called.
For multiple pools, create specific names with process.new_name() at startup.
pub fn layout(
config: SsrConfig,
template: fn(List(String), String) -> String,
) -> fn(String, json.Json) -> String
Wrap a template function to enable server-side rendering.
The template function receives:
head: List of HTML strings for the<head>sectionbody: The rendered HTML body content
If SSR fails, this automatically falls back to client-side rendering.
Example
fn my_layout(head: List(String), body: String) -> String {
"<!DOCTYPE html><html><head>"
<> string.join(head, "\n")
<> "</head><body>"
<> body
<> "<script src='/app.js'></script></body></html>"
}
// In handler:
|> inertia.response(200, ssr.layout(config, my_layout))
pub fn make_layout(
config: SsrConfig,
) -> fn(fn(List(String), String) -> String) -> fn(
String,
json.Json,
) -> String
Create a reusable layout function with SSR configuration baked in.
Call this once at startup and reuse the returned function in handlers.
Example
// At startup
let config = SsrConfig(
..ssr.default_config(),
module_path: ssr.priv_path("my_app", "ssr/ssr.js"),
)
let layout = ssr.make_layout(config)
// In handler
|> inertia.response(200, layout(my_template))
pub fn priv_path(app_name: String, path: String) -> String
Resolve a path relative to an OTP application’s priv directory.
Call this at application startup to get the absolute path for SsrConfig.
In Erlang releases, the priv directory location is unpredictable, so this
function uses the OTP application system to resolve it correctly.
Falls back to "priv/" <> path if the application is not loaded.
Example
let module_path = ssr.priv_path("my_app", "ssr/ssr.js")
let config = SsrConfig(..ssr.default_config(), module_path: module_path)
pub fn supervised(
config: SsrConfig,
) -> supervision.ChildSpecification(Nil)
Get a child specification for adding the SSR pool to your supervision tree.
The pool is registered under the name in config, allowing make_layout()
to look it up automatically.
Example
import gleam/otp/static_supervisor as supervisor
import inertia_wisp/ssr
pub fn start_app() {
let config = SsrConfig(
..ssr.default_config(),
module_path: ssr.priv_path("my_app", "ssr/ssr.js"),
)
supervisor.new(supervisor.OneForOne)
|> supervisor.add(ssr.supervised(config))
|> supervisor.start
}