Snapshot views
Draw a Horn snapshot on the server and hydrate it in the browser with @manta-eu/horns-kit.
What it is
@manta-eu/horns-kit draws a HornSnapshot, the rows a Horn’s views returned, laid out on the Horn’s grid. It
fetches nothing itself: whatever built the snapshot already ran the queries as the viewer. There are two ways to use
it:
- In a React tree. Render
HornSnapshotViewwith a snapshot.HornDrawndoes this for you, and fetches the snapshot too. - As HTML from a server.
renderHornEmbedturns a snapshot into markup plus the snapshot as inline JSON, andhydrateHornEmbedsmakes it live in the browser without a second request.
Written views and catalog cards need the host’s components, so the server draws a placeholder for them. The browser
fills the placeholder in when you pass writtenView and catalogCard to hydrateHornEmbeds, and keeps it otherwise.
Import
import { HornSnapshotView } from "@manta-eu/horns-kit";
import { renderHornEmbed } from "@manta-eu/horns-kit/server";
import { hydrateHornEmbeds } from "@manta-eu/horns-kit/client";
The kit needs react and react-dom 18 or newer.
Example
On the server, with a snapshot in hand:
const html = renderHornEmbed({ snapshot, mode: "embed", embedId: "open-tasks" });
In the browser, once the page has loaded:
hydrateHornEmbeds({
onRecoverableError: (error, embedId) => console.warn(`Horn ${embedId} re-rendered`, error),
});
hydrateHornEmbeds adopts every embed on the page and is safe to call again after you add more. An embed id is
lowercase letters, digits and hyphens, at most 64 characters.
A written view runs as code in the viewer’s browser, so a page that shows one needs a Content Security Policy that
allows 'unsafe-eval'. contentSecurityPolicy(sentryDsn) from @manta-eu/horns-server returns the policy it
expects. Pass undefined if you don’t report errors to Sentry.
Request
The kit sends no request. The Horns server can draw a whole page for you: post the Horn to
/d/<datastore>/horns/render.
POST /d/your-datastore/horns/render?mode=embed
Content-Type: application/json
{ "horn": { "…": "the Horn" }, "variables": { "status": "open" }, "viewIds": ["open-tasks"] }
variables and viewIds are optional. viewIds draws only those views, from 1 to 24 of them. The server fills the
/horn.html page among your static assets, which must hold a <!--horn-title--> and a <!--horn-embed-->
placeholder.
Response
renderHornEmbed, and the render route inside its page, produce one embed per Horn:
<div data-horn-embed="open-tasks" data-horn-mode="embed" data-horn-theme="light">…the drawn views…</div>
<script type="application/json" data-horn-snapshot="open-tasks">
{ "snapshotVersion": 1, "…": "…" }
</script>
The snapshot carries the Horn’s identity and grid, a views entry per view, the labels and links for every IRI
in the rows, the plans behind each view, the current variables, and generatedAt. Each view holds at most 1,000
rows.
After hydration the embed gets data-horn-ready="true". If React had to throw the server’s markup away and draw
again, it also gets data-horn-hydration="recovered", which is worth logging.