Reactions Vllnt
Reactions, votes, and likes on any resource — toggle, count by kind, and list reactors as a Bijection component
git clone https://github.com/bijectionhq/reactions-vllnt.git
cd reactions-vllnt
git checkout 58449db25af70c0524b9aad109f8e47727f42668Bijection repository access is required. Request access.
Description
Reactions, votes, and likes on any resource — toggle, count by kind, and list reactors as a Bijection component
Explore the package README for API concepts, configuration, and examples. Check the Bijection source for adaptation-specific setup.
Review the source and its dependencies before adopting this package. Source availability does not establish npm publication or runtime qualification.
Getting started
- Get the source at the revision above and read its package documentation.
- Check the required Bijection SDK version, dependencies, and external service credentials.
- Follow the package setup instructions and the guide to using components.
- Exercise the component in your deployment according to your project’s verification rules.
Open source
Maintained source: bijectionhq/reactions-vllnt. Based on vllnt/convex-reactions. License: MIT. Consult the repository for license terms, notices, and contributor attribution.
From the upstream publisher
Reactions, votes, and likes on any resource — toggle, count by kind, and list reactors as a Convex component
Explore the demo ↗Upstream overview
From the upstream package documentation.
@vllnt/convex-reactions records a subject’s reaction to an opaque resource reference. Define a fixed reaction vocabulary or allow your own kinds, then read counts and the current subject’s state through Convex queries.
react toggles one author/resource/kind edge. unreact removes it idempotently. counts returns totals by kind; hasReacted and myReactions support active button states; reactors paginates the subjects behind a reaction. Multiple mounts keep separate systems isolated.
Your host resolves identity, authorizes access, and decides whether reaction types may coexist. A toggle is not a safe blind retry: use explicit removal or host idempotency when needed. Moderation, anti-abuse rules, ranking, and UI remain application concerns.
Maintained by bntvllnt at VLLNT. Find the author on GitHub and X.
Use cases
From the upstream package documentation.
- Communities: add likes or emoji reactions to posts and comments.
- Learning apps: collect helpful or unclear feedback on lessons.
- Games: let players react to shared results or community creations.
- Product feedback: attach votes to suggestions or feedback items.
How it works
From the upstream package documentation.
react toggles one author/resource/kind edge. unreact removes it idempotently. counts returns totals by kind; hasReacted and myReactions support active button states; reactors paginates the subjects behind a reaction. Multiple mounts keep separate systems isolated.
Your host resolves identity, authorizes access, and decides whether reaction types may coexist. A toggle is not a safe blind retry: use explicit removal or host idempotency when needed. Moderation, anti-abuse rules, ranking, and UI remain application concerns.
Upstream package FAQ
Does @vllnt/convex-reactions handle authentication itself?
@vllnt/convex-reactions is auth-agnostic. The host application resolves the user identity and passes an opaque authorRef string to the component's react, hasReacted, and related methods. The component never reads session state or touches host tables, so it works with any auth strategy the host uses.
What happens if two users react to the same resource at the same time?
Each toggle is executed as a single Convex mutation transaction. The component enforces uniqueness of the (authorRef, resourceRef, kind) edge within that transaction, so concurrent react calls cannot produce duplicate edges or incorrect counts. The counts query reflects the transactionally correct state.
Can I restrict which reaction kinds are allowed?
Yes. Pass allowedKinds as an array of strings when constructing the Reactions instance, for example allowedKinds: ['up', 'down'] or a fixed emoji list. Any kind not in that list is rejected at the component boundary before any database write occurs. Omit allowedKinds entirely to allow freeform kinds.
How do I install and wire up @vllnt/convex-reactions in a Convex project?
Run npm install @vllnt/convex-reactions (peer dependency: convex@^1.41.0), then import the convex.config and call app.use(reactions) in convex/convex.config.ts. Instantiate the Reactions class with components.reactions in any host mutation or query file and call its methods from your own Convex functions.
Does this component provide any frontend React hooks?
@vllnt/convex-reactions is a backend-only component with no ./react entry point. Reaction tallies and reactor lists are consumed on the frontend by calling useQuery against the host's own re-exported counts and reactors query functions, which is the standard Convex reactive query pattern.
Use with agents and CLI
Give your coding agent the component’s source identity and setup guidance.
Help me integrate Reactions Vllnt with Bijection.
Read: https://bijection.com/components/reactions-vllnt/SKILL.md
Check SDK compatibility and dependency availability.
Follow this project's execution and verification rules.Upstream README.md
View repository ↗Documentation from vllnt/convex-reactions, recorded October 23, 2026. APIs and setup may differ in the Bijection adaptation.
@vllnt/convex-reactions
Reactions, votes, and likes on any resource, as a Convex component.
const reactions = new Reactions(components.reactions);
await reactions.react(ctx, authorRef, resourceRef, "up"); // toggle the edge in one transaction
await reactions.counts(ctx, resourceRef); // [{ kind, count }], reactive
await reactions.hasReacted(ctx, authorRef, resourceRef, "up");
A reaction is the opaque edge (authorRef, resourceRef, kind). react toggles it (add if absent,
remove if present); counts tallies a resource per kind; reactors pages who reacted. One edge per
subject per kind per resource is enforced in the mutation transaction, so toggles and counts stay
correct under concurrency.
Features
- Toggle —
react(authorRef, resourceRef, kind)adds the edge if absent and removes it if present in one transaction, returning{ reacted, action }. - Idempotent remove —
unreact(authorRef, resourceRef, kind)deletes the edge; removing an absent one is a safe no-op (false). - Count by kind —
counts(resourceRef)returns[{ kind, count }, ...]sorted by kind; no reactions returns[]. - List reactors —
reactors(resourceRef, kind, paginationOpts)pages who reacted with one kind, oldest first. Reactive in a Convex query. - Per-subject state —
hasReactedandmyReactionsgive the host the subject's own reaction state for rendering controls. - Configurable vocabulary —
allowedKindspins the reaction set (["up", "down"], a fixed emoji list); an unknown kind is rejected at the boundary. Omit for freeform. - Server-sourced time —
createdAtis stamped from the server clock; a caller can't supply a timestamp. - Mount-safe — correct under multiple named
app.usemounts; each instance is an isolated sandbox.
Installation
pnpm add @vllnt/convex-reactions
Peer dependency: convex@^1.41.0.
Usage
// convex/convex.config.ts
import { defineApp } from "convex/server";
import reactions from "@vllnt/convex-reactions/convex.config";
const app = defineApp();
app.use(reactions);
export default app;
// convex/reactions.ts — host owns auth; pass opaque refs in.
import { components } from "./_generated/api";
import { mutation, query } from "./_generated/server";
import { paginationOptsValidator } from "convex/server";
import { v } from "convex/values";
import { Reactions } from "@vllnt/convex-reactions";
const reactions = new Reactions(components.reactions, {
allowedKinds: ["up", "down"], // pin the vocabulary (optional)
});
export const vote = mutation({
args: { postId: v.string(), kind: v.string() },
handler: async (ctx, { postId, kind }) => {
const userId = await requireUser(ctx); // host auth
return reactions.react(ctx, userId, postId, kind);
},
});
export const tally = query({
args: { postId: v.string() },
handler: (ctx, { postId }) => reactions.counts(ctx, postId),
});
API Reference
| Method | Kind | Result |
|---|---|---|
react(ctx, authorRef, resourceRef, kind) | mutation | { reacted, action } (action: "added" | "removed") |
unreact(ctx, authorRef, resourceRef, kind) | mutation | boolean (true if an edge was removed) |
counts(ctx, resourceRef) | query | { kind, count }[] (sorted by kind) |
hasReacted(ctx, authorRef, resourceRef, kind) | query | boolean |
myReactions(ctx, authorRef, resourceRef) | query | string[] (kinds the subject placed) |
reactors(ctx, resourceRef, kind, paginationOpts) | query | PaginationResult<ReactionView> |
Full reference: docs/API.md.
React
Backend-only — no ./react entry. A reaction tally or reactor list is an ordinary reactive useQuery over the host's own re-exported counts / reactors refs.
Security
- Auth-agnostic — the host resolves identity and decides who may react.
- Tables sandboxed — reached only through the exported functions; never touches host or sibling tables.
- Uniqueness is transactional + time is server-sourced; refs and
kindstay opaque to the component.
See docs/API.md.
Testing
pnpm test # single run
pnpm test:coverage # enforced 100% on covered files
Tests run against the real component runtime via convex-test (@edge-runtime/vm), not mocks.
Contributing
See CONTRIBUTING.md.
Author
Built by bntvllnt · bntvllnt.com · X @bntvllnt
Part of the @vllnt Convex component fleet — vllnt.com
If this is useful, sponsor the work.
License
MIT — see LICENSE.