@lst97-oss/last-comments-widgets
@lst97-oss/last-comments-widgets
Drop-in comment widgets for a last-comments server: the same widget implemented natively for React, Vue, Angular and plain HTML, on one framework-free API client and one self-contained Tailwind v4 + shadcn base-nova theme.
One package, one version, subpath exports — you install only the framework you use.
Install
bun add @lst97-oss/last-comments-widgets # or npm install @lst97-oss/last-comments-widgets
Then add the runtime libraries your framework's widget imports — every other framework's libraries are optional peers and are never installed:
# React
bun add @base-ui/react lucide-react class-variance-authority clsx tailwind-merge
# …plus shiki, only if comment code fences should be syntax-highlighted
bun add shiki
# …and katex / mermaid, only if comments may carry `$…$` math or mermaid diagrams
bun add katex mermaid
# Vue
bun add @lucide/vue reka-ui clsx tailwind-merge
# …plus katex / mermaid for the same optional renderers
bun add katex mermaid
# Angular
bun add @angular/cdk @lucide/angular
# Plain HTML: nothing more — the widget bundle and the theme are self-contained
Only your framework is a peer dependency (React 19, Vue 3.5 or Angular 22); the others are optional peers and are never loaded.
Usage
React 19:
import { Comments } from '@lst97-oss/last-comments-widgets/react'
import '@lst97-oss/last-comments-widgets/react/styles.css'
;<Comments site="demo" baseUrl="https://comments.example.com" turnstileSiteKey="…" />
Vue 3:
<script setup lang="ts">
import { Comments } from '@lst97-oss/last-comments-widgets/vue'
import '@lst97-oss/last-comments-widgets/vue/styles.css'
</script>
<template>
<Comments site="demo" base-url="https://comments.example.com" turnstile-site-key="…" />
</template>
Angular 22:
import { LcCommentsComponent } from '@lst97-oss/last-comments-widgets/angular'
import '@lst97-oss/last-comments-widgets/angular/styles.css'
@Component({ imports: [LcCommentsComponent], template: `<lc-comments site="demo" baseUrl="…" />` })
export class AppComponent {}
Plain HTML, no build step:
<link rel="stylesheet" href="https://unpkg.com/@lst97-oss/last-comments-widgets/dist/vanilla/styles.css" />
<div data-lc-comments data-lc-site="demo" data-lc-base-url="https://comments.example.com"></div>
<script type="importmap">
{ "imports": { "marked": "https://unpkg.com/marked/lib/marked.esm.js" } }
</script>
<script type="module" src="https://unpkg.com/@lst97-oss/last-comments-widgets/dist/vanilla/index.js"></script>
Entry points
| Import | What it gives you |
|---|---|
@lst97-oss/last-comments-widgets · …/client |
The framework-free API client: createClient, listCommentsPage, loadThreadWindow, buildCommentTree, describeError, renderMarkdown, themeVarsToStyle, isEditWindowOpen, canModerate and the exported types |
…/react |
Comments, CommentViewer, VoteButtons, CommentsTheme |
…/vue |
The same components as Vue SFCs |
…/angular |
LcCommentsComponent, LcCommentViewerComponent, the vote buttons and the Turnstile directive |
…/vanilla |
mountComments, renderCommentViewer for plain HTML |
…/theme.css |
The theme tokens on their own |
…/{react,vue,angular,vanilla}/styles.css |
The compiled widget stylesheet for that framework |
Highlights
- Threads page by thread, not by row: a page shows
limit(default 20) top-level comments with their replies, filled before it renders, withPreviousserved from memory. Loading states are skeletons, andlazydefers every request until the widget scrolls into view. - Every post carries a Cloudflare Turnstile token: guests add a name, signed-in viewers post with their session and get votes and Edit/Delete. The composer is disabled while the session probe is in flight so a comment can never post with a guessed identity.
theme="light | dark | system"plus per-instancethemeVars, scoped with native CSS@scopeso importing the widget CSS does not touch your page's fonts, colors or margins.- Every failure renders one alert block with copy from
describeError(), carryingdata-lc-error/data-error-code. - Markdown (GitHub-flavored) with raw HTML stripped and
http(s)/mailtolinks only. Optionalmath($…$/$$…$$via KaTeX) andmermaid(```mermaid fences as diagrams) renderers are off by default and load their library from a lazy import only when a body needs one;codeHighlight(default on) controls the React Shiki upgrade. viewerRoleadds inline, server-authorized moderation;CommentViewerrenders a read-only list.
Full documentation — props, theming, pagination, moderation, error codes and the client API — lives in the repository README.
Requirements
- Register the page origin in the server's
site_originstable, or every call fails withORIGIN_NOT_ALLOWED. - The API never returns
Access-Control-Allow-Credentials, so a cross-origin embed cannot list, post, vote or moderate: route/api/*through a same-origin proxy. Moderation additionally needsOriginrewritten for/api/moderation, whose POST is origin-checked. - A browser with
@scopesupport.
Licence
MIT — Copyright (c) 2026 lst97-oss
Related Packages
Arcana Labs UI Components — a typed, framework-native shadcn-style component lib...
A framework-agnostic developer toolbar for interacting with LaunchDarkly during...
A lightweight, dependency-free grouped autocomplete suggestion input (Vanilla TS...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.6.2 | ~3.5.44 | MIT | |
| 0.6.1 | ~3.5.44 | MIT | |
| 0.5.0 | ~3.5.44 | MIT | |
| 0.4.3 | ~3.5.44 | MIT | |
| 0.4.2 | ~3.5.44 | MIT | |
| 0.4.1 | ~3.5.44 | MIT | |
| 0.4.0 | ~3.5.44 | MIT | |
| 0.3.0 | ~3.5.44 | MIT | |
| 0.2.1 | ^3.5.44 | MIT | |
| 0.2.0 | ^3.5.0 | MIT | |
| 0.1.0 | ^3.5.0 | MIT |
Pre-releases (1)
| Version | Released | License |
|---|---|---|
| 0.0.0-stage |