Emoji buttons on each post. Click to react. A dashboard tells you which posts people actually care about.
Updated March 2026
Static changelogs are great until you want to know if anyone actually reads them. This adds emoji reactions to each post. Users click a thumbs-up or rocket, and the reaction goes to Sutrena with the post_id. A dashboard tells you which posts got the most engagement and which emoji types people prefer. No database, no API server. Just Astro, Cloudflare Pages, and one Sutrena form. An agent can set this up end-to-end: create the reaction form with hidden post_id field, build the analytics dashboard with charts grouped by post and reaction type.
Architecture
| Tool | Role | Cost |
|---|---|---|
| Astro | Static site generator | Free (open source) |
| Cloudflare Pages | Static site hosting | Free |
| Sutrena | Reaction storage, analytics dashboard | $9/mo (Builder) |
Total cost: $9/mo
Builder starts at $9/month for 50 projects. Pro at $29/month covers 200 projects and all your other work too. Build five blueprints on Pro and it is still $29/month. That is where the value is — at scale.
Astro generates your changelog as static HTML. Each post page includes a ReactionBar component with client:load so it hydrates in the browser. Click an emoji, it POSTs the post_id and reaction type to Sutrena. publicResults is on so the component can fetch current counts.
The dashboard has a bar chart of reactions by type and another by post. So you see both what reactions people use and which posts they react to.
Form Definition
Hidden post_id and a reaction type. Emoji names stored as text (thumbsup, heart, tada, rocket) for clean grouping in the dashboard. publicResults lets the frontend show counts without auth.
{
"name": "Changelog Reactions",
"fields": [
{
"name": "post_id",
"type": "hidden"
},
{
"name": "reaction",
"label": "Reaction",
"type": "select",
"options": [
"thumbsup",
"heart",
"tada",
"rocket"
],
"required": true
}
],
"publicResults": true
}Dashboard Definition
Total reactions, which emoji types get used most, and which posts get the most reactions. The answers are kinda interesting.
{
"version": 1,
"widgets": [
{
"type": "metric_card",
"title": "Total Reactions",
"value": "count(*)"
},
{
"type": "bar_chart",
"title": "Reactions by Type",
"groupBy": "reaction"
},
{
"type": "bar_chart",
"title": "Reactions by Post",
"groupBy": "post_id"
}
]
}Frontend Integration
An Astro web component with emoji buttons. Each button POSTs post_id and reaction to Sutrena. Uses a custom element so it works with Astro's island architecture. Replace frm_YOUR_FORM_ID with yours.
---
// src/components/ReactionBar.astro
// Use with client:load to hydrate in the browser
---
<reaction-bar data-post-id={Astro.props.postId}></reaction-bar>
<script>
class ReactionBar extends HTMLElement {
connectedCallback() {
const postId = this.dataset.postId;
const FORM_ID = "frm_YOUR_FORM_ID";
const emojis = [
{ key: "thumbsup", display: "\u{1F44D}" },
{ key: "heart", display: "\u{2764}\u{FE0F}" },
{ key: "tada", display: "\u{1F389}" },
{ key: "rocket", display: "\u{1F680}" },
];
this.innerHTML = emojis
.map(
(e) =>
`<button data-reaction="${e.key}" style="font-size:1.5rem;cursor:pointer;background:none;border:none;padding:0.25rem;">${e.display}</button>`
)
.join("");
this.querySelectorAll("button").forEach((btn) => {
btn.addEventListener("click", async () => {
const reaction = btn.dataset.reaction;
const res = await fetch(
`https://sutrena.com/api/forms/${FORM_ID}/submit`,
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ post_id: postId, reaction }),
}
);
if (res.ok) btn.style.opacity = "0.5";
});
});
}
}
customElements.define("reaction-bar", ReactionBar);
</script>By default, yes. To limit it, add a hidden user_id field (from cookies or local storage) and set uniqueBy: ["post_id", "user_id"]. For anonymous reactions, unlimited is usually fine.
Fetch submissions filtered by post_id from the public API, group by reaction type in your frontend, and display the count next to each button. A few lines of JavaScript.
Yes. The concept is the same for Hugo, Eleventy, Jekyll, whatever you use. Swap the Astro component for a vanilla JS script or framework-specific component.
No. The page still loads as static HTML. The reaction component hydrates after load and makes one fetch call. Clicks are independent requests that do not block the UI.
Sutrena is the web runtime for AI agents. Three primitives — pages, forms, and dashboards — accessible through one API. Your agent creates web artifacts, humans interact with them, and your agent gets the data back. Framework-agnostic. Works from any MCP client or HTTP client.
1. Get a trial key (no auth, no signup)
curl -X POST https://sutrena.com/api/trial2. Create a form + dashboard from a template
curl -X POST https://sutrena.com/api/forms \
-H "Authorization: Bearer st_trial_xxx" \
-H "Content-Type: application/json" \
-d '{"templateId": "waitlist", "createDashboard": true}'Get a trial API key instantly with no signup, or create an account for the full experience.