Emoji Reactions
Emoji reaction buttons for your posts. One reaction per reader, optional counts, no cookies and no raw IPs stored.
Plugin
Emoji Reactions
Emoji reaction buttons for your posts. One reaction per reader, optional counts, no cookies and no raw IPs stored.
Description
Reactions adds emoji reaction buttons (💗 🤔 😗 by default) to your posts. Each reader picks one reaction per post: clicking it again removes it, clicking another one switches.
Features
- Up to 6 buttons, configured from the admin page (
key=emoji:label). - Optional labels next to each emoji, and optional public counts.
- An admin page that lists your 10 most-reacted posts.
- The admin page follows the admin locale (English or Japanese).
- No extra database: data lives in the plugin’s own EmDash storage.
- No capabilities and no network access requested.
Privacy
- Readers are recognised by a SHA-256 hash of their IP address and User-Agent with a random per-site salt. The IP and User-Agent themselves are never stored.
- Stored per vote: post ID, the hash, the chosen reaction and a timestamp. No cookies.
Note: the plugin does not render anything on its own. After installing it, add the
Reactions.astrocomponent to your post template (see Installation).
Requirements
- EmDash 1.x on Cloudflare (D1)
- Workers Paid plan (sandboxed plugins run in the Worker Loader binding)
Installation
1. Install the plugin
Install Reactions from this page with Open in EmDash, or build it from source:
pnpm install
pnpm run build # dist/
When building from source, add the package to your site (pnpm add file:../emdash-plugin-emoji-reactions) and list it in astro.config.mjs under sandboxed: [reactions].
2. Enable the plugin sandbox
Sandboxed plugins need the Worker Loader binding. In wrangler.jsonc (commented out in the templates):
"worker_loaders": []
Make sure src/worker.ts exports PluginBridge (the *-cloudflare templates already do), and that astro.config.mjs sets sandboxRunner: sandbox().
3. Add the buttons to your post template (required)
Copy site-snippets/Reactions.astro into src/components/ and place it on your post page:
---
import Reactions from "../../components/Reactions.astro";
// `post` comes from getEmDashEntry()
---
Use post.data.id (the database ID), not post.id (the slug). Slugs change when a post is renamed, and translated slugs contain /, which the plugin rejects.
Props: contentId (required), heading (empty string hides it), label (accessible name of the button group). Set --rx-accent in your CSS to match your accent colour.
4. Choose your buttons
Open Reactions in the admin sidebar and edit the field, e.g.:
love=💗:Love, hmm=🤔:Hmm, whistle=😗:Whoa
Up to 6 entries. Keys start with a lowercase letter. Counts are stored per key: renaming a key starts a new button; removing one hides it but keeps its counts.
FAQ
The buttons don’t appear on my site
- Check that
Reactions.astrois added to your post template. The plugin itself renders nothing. - Open the browser console. Failures are logged as
[reactions] state: HTTP 403 CSRF_REJECTED .... - If you copied
dist/into your site instead of installing the package, check thatdist/index.mjscontains"routes". Without it, EmDash treats the public routes as private and rejects requests.
Can I hide the counts?
Yes. Turn off Show counts in the admin page. Counts are then also left out of the public API, so they can’t be read from the page. They keep being counted and stay visible to you in the admin page.
Two people in my home/office count as one reader
Readers behind the same IP using the same browser are treated as one. This is a casual reaction button, not a voting system.
Can people spam the button?
Each reader has one reaction per post, so repeated clicks only toggle it. The routes are public, though. If needed, add a Cloudflare Rate Limiting rule for /_emdash/api/plugins/reactions/react.
Does it work in local development?
Yes. When no client IP is available, the browser sends a random ID kept in localStorage (rx-visitor) instead.
Security
Reporting a vulnerability
Please report security issues privately through GitHub private vulnerability reporting, not in public issues.
What the plugin can access
- No capabilities and no network access. The plugin can’t read or change your content, media, users or settings outside its own storage, and it can’t call other services.
- It stores data only in its own EmDash storage: per-post counts, one record per vote, and its button settings.
Data stored about readers
- A truncated SHA-256 hash of the reader’s IP address and User-Agent, combined with a random per-site salt. The IP address and User-Agent themselves are never stored.
- Per vote: post ID, that hash, the chosen reaction and a timestamp. No cookies.
Public routes
state (GET) and react (POST) are public. Both validate their input: contentId must match [A-Za-z0-9_\-:.], reactions must be a configured key, and request bodies are capped at 1 KB. The admin route is private. To limit abuse, add a Cloudflare Rate Limiting rule for /_emdash/api/plugins/reactions/react.
Changelog
0.2.1 — 2026-10-18
- Added the registry icon.
0.2.0 — 2026-10-15
First release on the EmDash plugin registry.
- Show labels and Show counts switches in the admin page. Labels are hidden by default and kept as tooltips and screen-reader text. With counts hidden, the public API leaves them out too.
- Requires EmDash 1.x. Button settings move to
ctx.settings; buttons saved in 0.1.x carry over. - The admin page follows the admin locale (English or Japanese). Default labels are now English (
Love/Hmm/Whoa); keys are unchanged, so existing counts stay attached. Reactions.astro: logs failures to the browser console instead of hiding silently, adds alabelprop and the--rx-accentCSS variable, and documents passingpost.data.id.pnpm run testnow ends with a build, sodist/always includes the public route declarations.
0.1.0
- Initial version (not published to the registry).