inertia_wisp/inertia
Inertia.js adapter for the Gleam Wisp web framework.
This module provides a type-safe API for building Inertia.js responses. See the inertiajs docs for more details on the inertiajs protocol and usage patterns.
Quick Example
import inertia_wisp/html
type UserProps {
UserProps(name: String, email: String, posts: Option(List(Post)))
}
fn show_user(req: Request, user_id: Int) -> Response {
let props = UserProps(
name: "Alice",
email: "alice@example.com",
posts: None,
)
req
|> inertia.response_builder("Users/Show")
|> inertia.props(props, encode_user_props)
|> inertia.lazy("posts", fn(props) {
Ok(UserProps(..props, posts: Some(load_posts(user_id))))
})
|> inertia.response(200, html.default_layout)
}
Prop Behaviors
- Default: Included on standard visits, available on partial reloads
- Lazy: Evaluated only when needed (not in partial reloads unless requested)
- Optional: Excluded by default, included only when explicitly requested
- Always: Always included, even on partial reloads
- Defer: Loaded in a separate request after initial page render
- Merge: Client-side merging for efficient data updates
Types
pub type InertiaResponseBuilder(props) =
@internal InertiaResponseBuilder(props)
Values
pub fn always(
builder: InertiaResponseBuilder(props),
field_name: String,
) -> InertiaResponseBuilder(props)
Configure a prop to always be included, even in partial reloads.
Always props are included in every request, regardless of what fields are requested in a partial reload. Useful for shared data like auth user.
Example
|> inertia.always("auth") // Always send current user
pub fn clear_history(
builder: InertiaResponseBuilder(props),
) -> InertiaResponseBuilder(props)
Clear the browser history state after this response.
Useful for preventing authenticated responses from being seen after logout.
pub fn defer(
builder: InertiaResponseBuilder(props),
field_name: String,
resolver: fn(props) -> Result(props, dict.Dict(String, String)),
) -> InertiaResponseBuilder(props)
Configure a prop to be deferred (loaded after initial page render).
Deferred props are advertised in the initial response metadata, and the frontend automatically requests them in a subsequent request. Perfect for expensive operations that shouldn’t block initial page load.
Example
|> inertia.defer("analytics", fn(props) {
process.sleep(2000) // Expensive operation
Ok(DashboardProps(..props, analytics: Some(generate_report())))
})
pub fn defer_in_group(
builder: InertiaResponseBuilder(props),
field_name: String,
group: String,
resolver: fn(props) -> Result(props, dict.Dict(String, String)),
) -> InertiaResponseBuilder(props)
Configure a deferred prop with a specific group name.
Grouped deferred props can be loaded together in a single request, reducing the number of round trips to the server.
pub fn encrypt_history(
builder: InertiaResponseBuilder(props),
) -> InertiaResponseBuilder(props)
Encrypt the history entry for this page.
Use this for authenticated responses.
pub fn errors(
builder: InertiaResponseBuilder(props),
errors: dict.Dict(String, String),
) -> InertiaResponseBuilder(props)
Add validation errors to the response.
Errors are automatically included in the props sent to the frontend and stored in a signed cookie during redirects to support the Post/Redirect/Get pattern.
Example
case validate(form_data) {
Error(errors) -> {
req
|> inertia.response_builder("ContactForm")
|> inertia.errors(errors)
|> inertia.redirect("/contact")
}
}
pub fn lazy(
builder: InertiaResponseBuilder(props),
field_name: String,
resolver: fn(props) -> Result(props, dict.Dict(String, String)),
) -> InertiaResponseBuilder(props)
Configure a prop to be lazy-evaluated.
Lazy props are only evaluated when explicitly requested in a partial reload. The resolver receives the current props and returns updated props.
Example
|> inertia.lazy("comments", fn(props) {
Ok(BlogPostProps(..props, comments: Some(load_comments())))
})
pub fn merge(
builder: InertiaResponseBuilder(props),
field_name: String,
match_on match_on: option.Option(List(String)),
deep deep: Bool,
) -> InertiaResponseBuilder(props)
Configure a prop to use client-side merging behavior.
Merge props enable efficient data updates on the frontend, useful for pagination, infinite scroll, or updating lists without replacing them.
Parameters
match_on: Optional list of keys to match items by (e.g.,Some(["id"]))deep: Whether to perform deep merging of nested objects
Example
|> inertia.merge("users", match_on: Some(["id"]), deep: False)
pub fn optional(
builder: InertiaResponseBuilder(props),
field_name: String,
) -> InertiaResponseBuilder(props)
Configure a prop to be optional (excluded by default).
Optional props are only included when explicitly requested in a partial reload. Useful for expensive data that’s rarely needed.
pub fn props(
builder: InertiaResponseBuilder(a),
props: props,
encode: fn(props) -> dict.Dict(String, json.Json),
) -> InertiaResponseBuilder(props)
Set the props data and encoder for this response.
The encoder function should return a Dict(String, Json) containing all
fields that should be sent to the frontend.
Example
fn encode_user_props(props: UserProps) -> Dict(String, Json) {
dict.from_list([
#("name", json.string(props.name)),
#("email", json.string(props.email)),
])
}
pub fn redirect(
builder: InertiaResponseBuilder(props),
url: String,
) -> response.Response(wisp.Body)
Create a redirect response (303 See Other).
If errors are present in the builder, they will be stored in a signed cookie and automatically retrieved on the next request.
Example
req
|> inertia.response_builder("Users/Create")
|> inertia.redirect("/users")
pub fn response(
builder: InertiaResponseBuilder(props),
status: Int,
layout: fn(String, json.Json) -> String,
) -> response.Response(wisp.Body)
Build the final HTTP response with the given status code and HTML layout.
This evaluates all prop resolvers, applies filtering based on the request type, and generates either a JSON response (for Inertia requests) or an HTML response with embedded JSON (for initial page loads).
The layout function is used to generate the HTML document for initial page loads. It receives the component name and the Inertia page data as JSON.
Example
import inertia_wisp/html
|> inertia.response(200, html.default_layout)
pub fn response_builder(
req: request.Request(wisp.Connection),
component: String,
) -> InertiaResponseBuilder(Nil)
Create a new Inertia response builder.
This initializes the builder with no props (type parameter is Nil).
Call props() to set the actual props and change the type parameter.
Example
req
|> inertia.response_builder("Dashboard")
|> inertia.props(dashboard_props, encode_dashboard_props)
|> inertia.response(200, html.default_layout)
pub fn version(
builder: InertiaResponseBuilder(props),
version: String,
) -> InertiaResponseBuilder(props)
Set an asset version for automatic cache busting.
When set, the server will check incoming Inertia requests for version mismatches.
If the client’s version (from the X-Inertia-Version header) doesn’t match the
server’s version, a 409 Conflict response is returned with the X-Inertia-Location
header, causing the client to perform a full page reload to get fresh assets.
If no version is set, version checking is disabled and all requests proceed normally.
Example
|> inertia.version("abc123")
Common patterns include using a build hash or timestamp:
- Git commit hash:
"a1b2c3d" - Build timestamp:
"20250107120000" - Version number:
"1.2.3"