halyard_server 0.1.0

RPC for the Halyard web framework.
docs.rs failed to build halyard_server-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

halyard

halyard is a full-stack Rust web framework: server-side rendering, hydration and fine-grained reactivity, with an axum integration. It is its own project. It began on 2026-09-22 as a fork of Leptos 0.8.20 (MIT, © 2022 Greg Johnston — see LICENSE and NOTICE) and no longer tracks it. Every crate is renamed (leptoshalyard, leptos_routerhalyard_router, tachyshalyard_tachys, …; full map below) and published on crates.io under those names (halyard = "0.1"); its build tool is cargo-halyard.

Why it began as a fork

We hit six defects in production (reproduced in Chromium and WebKit) that are better fixed at the source than worked around in every application:

  1. WASM file name baked in at compile time. Upstream decided between <name>.wasm and <name>_bg.wasm with option_env!("LEPTOS_OUTPUT_NAME"), so a server built by plain cargo build requested a file the build tool never wrote (404, hydration never ran). halyard resolves the name at runtime from HalyardOptions (new wasm_file_name option, default output_name); no option_env!/env! influences rendered output any more (leptos/src/hydration/mod.rs, leptos_config).
  2. --cfg erase_components mismatch. Debug builds run through the build tool are compiled with --cfg erase_components, which changes the hydration marker comments the server emits; a server built without it made the client panic with "expected a marker node". halyard records the server's mode in a <meta name="halyard-render-mode"> tag and the client checks it before hydrating: on a mismatch it logs one clear error naming both modes and how to fix it, then skips hydration — no panic (leptos/src/hydration/mod.rs, leptos/src/mount.rs, leptos/tests/render_mode.rs).
  3. Hydration mismatches were panics with useless context. halyard logs the view's source location (in debug / --cfg halyard_debuginfo builds), what was expected, what was found (tag/text snippet) and the DOM path, then abandons hydration cleanly and renders the app on the client instead. The old behaviour is behind the panic-on-hydration-mismatch feature (tachys/src/hydration.rs).
  4. Bootstrap script had no rejection handler (WebKit: "Unhandled Promise Rejection: TypeError: Load failed" when navigating away mid-load). The inline script now ends in a .catch that logs one concise console.warn.
  5. WebKit downloaded the WASM twice because <link rel="preload" as="fetch"> is not matched against wasm-bindgen's fetch(). halyard drops the preload and instead starts the fetch() itself, immediately, from a classic inline script, handing the pending Response to init — exactly one request in every browser, started as early as the preload was.
  6. Two unmaintained proc-macro helpers. paste is replaced by pastey; proc-macro-error2 is replaced by halyard_macro_diagnostics (a few dozen lines on syn::Error::to_compile_error). Because rstml pulled proc-macro-error2 in through syn_derive, both are vendored under third_party/ with that dependency removed. Neither crate is in the lockfile.

Crate map

upstream (dir kept) halyard
leptos (leptos/) halyard
leptos_macro halyard_macro
leptos_router (router/) halyard_router
leptos_router_macro (router_macro/) halyard_router_macro
leptos_meta (meta/) halyard_meta
leptos_axum (integrations/axum/) halyard_axum
leptos_actix (integrations/actix/) halyard_actix
leptos_integration_utils (integrations/utils/) halyard_integration_utils
leptos_server, leptos_config, leptos_dom, leptos_hot_reload halyard_server, halyard_config, halyard_dom, halyard_hot_reload
tachys halyard_tachys
reactive_graph, reactive_stores, reactive_stores_macro halyard_reactive_graph, halyard_reactive_stores, halyard_reactive_stores_macro
hydration_context halyard_hydration_context
server_fn, server_fn_macro, server_fn_macro_default halyard_server_fn, halyard_server_fn_macro, halyard_server_fn_macro_default
any_spawner, either_of, next_tuple, or_poisoned, const_str_slice_concat halyard_any_spawner, halyard_either_of, halyard_next_tuple, halyard_or_poisoned, halyard_const_str_slice_concat
oco_ref (oco/) halyard_oco
throw_error (any_error/) halyard_throw_error
— (new) halyard_macro_diagnostics
rstml 0.12.1, syn_derive 0.2.0 (vendored, third_party/) halyard_rstml, halyard_syn_derive

Inside halyard the re-export names are unchanged: halyard::tachys, halyard::server_fn, halyard::reactive, halyard::prelude::*, and the view!, #[component], #[server] macros keep their names. Types named Leptos* are now Halyard* (HalyardOptions, HalyardRoutes, …).

Configuration

Every runtime setting is read from HALYARD_<NAME> with the legacy LEPTOS_<NAME> as a fallback (HALYARD_OUTPUT_NAME / LEPTOS_OUTPUT_NAME, ..._SITE_ROOT, ..._SITE_PKG_DIR, ..._SITE_ADDR, ..._RELOAD_PORT, ..._ENV, ..._HASH_FILES, ..._HASH_FILE_NAME, ..._WATCH, and the new ..._WASM_FILE_NAME), so existing cargo-leptos configurations and deployments keep working. Likewise get_configuration(Some("Cargo.toml")) reads [package.metadata.halyard] and falls back to [package.metadata.leptos].

Checks

What CI runs (.github/workflows/ci.yml), each crate tested on its own (see the known issue below):

cargo fmt --check
cargo clippy --workspace -- -D warnings
scripts/panic-ratchet.sh       # panic sites per crate may only fall (panic-baseline.txt)
cargo test -p <crate>          # for each crate
cargo check -p halyard --no-default-features --features hydrate --target wasm32-unknown-unknown
cargo test -p halyard --features ssr --test render_mode
RUSTFLAGS="--cfg erase_components" cargo test -p halyard --features ssr --test render_mode

Before a release, cargo package --workspace packages and verifies every crate against crates.io the way cargo publish will. A dev-dependency on a crate that is published later must be path-only (no version), or the publish of the earlier crate fails.

examples/ssr_modes_axum is kept as an SSR + hydration smoke test for the sibling build tool (cargo-halyard).

The Leptos book (https://book.leptos.dev/) still describes most of the API, apart from the crate names and the changes listed above. See ARCHITECTURE.md for how the crates fit together.

Project policy

halyard is our framework, taken wherever the applications built on it need; it does not follow Leptos.

  • Refactor freely. Rename, restructure, delete and redesign. Nothing is kept mergeable with Leptos, and nothing is merged or ported from it on a schedule.
  • Improve as we go. When an application needs something (a clearer error, a safer default, a missing hook), change halyard rather than working around it.
  • No panics, ever. A panic in the browser kills the application (release wasm is built with panic = "abort"); on the server it fails a request. Library code returns typed errors (Result, thiserror enums) or recovers visibly (log, fall back to client rendering, render nothing), and uses the strongest types that are practical so the impossible states cannot be written. The inherited code does not meet this yet: scripts/panic-ratchet.sh counts every unwrap, expect, panic!, unreachable!, todo!, unchecked index and unchecked arithmetic per crate, CI fails if a count rises, and each crate's lints go to deny once its count reaches zero. The hydration and server request paths go first.
  • Never write to Leptos. The old upstream remote is push-disabled; we do not open issues or pull requests there.
  • Attribution stays. LICENSE (MIT, © 2022 Greg Johnston) and NOTICE travel with every copy.

Known issues (fork backlog)

  • cargo test --workspace fails 8 targets because Cargo unifies the sandboxed-arenas feature (enabled by the axum integration's tests) into crates whose tests assume it is off. Every crate passes when tested on its own (cargo test -p <crate>), which is how CI runs them. Inherited from upstream; to be fixed by making those tests feature-aware.