Introduction
A beautiful, yet simple library that brings a comment area into your blog.
Note that it is not a SaaS, you will need a database (SQLite, PostgreSQL, MySQL) to continue.
Client
Installation
npm install @fuma-comment/react
Currently, we provide a React.js client API.
"use client";
import { signIn } from "next-auth/react";
import { Comments } from "@fuma-comment/react";
export function CommentsWithAuth() {
return (
<Comments
// comments are grouped by page
page="default"
auth={{
type: "api",
// function to sign in
signIn: () => void signIn("github"),
}}
/>
);
}
Fuma Comment doesn't force an Auth system on your app, you can use your own Auth system, like integrating with BetterAuth.
By default, it calls an API endpoint to get authentication data, you will need to provide a signIn
function for user to sign in.
Styling
All components are pre-styled with Tailwind CSS, notice that it also normalizes your CSS (Preflight).
import "@fuma-comment/react/style.css";
For projects using Tailwind CSS v4, use the Tailwind CSS preset instead:
@import "tailwindcss";
@import "@fuma-comment/react/preset.css";
Backend
npm install @fuma-comment/server
Database
You need a database to persist comments.
Fuma Comment has built-in support for multiple ORMs, you can copy the required schemas:
import {
pgTable,
varchar,
boolean,
integer,
serial,
json,
timestamp,
primaryKey,
index,
} from "drizzle-orm/pg-core";
export const roles = pgTable("roles", {
userId: varchar("userId", { length: 256 }).primaryKey(),
name: varchar("name", { length: 256 }).notNull(),
canDelete: boolean("canDelete").notNull(),
});
export const comments = pgTable("comments", {
id: serial("id").primaryKey().notNull(),
page: varchar("page", { length: 256 }).notNull(),
thread: integer("thread"),
author: varchar("author", { length: 256 }).notNull(),
content: json("content").notNull(),
timestamp: timestamp("timestamp", { withTimezone: true })
.defaultNow()
.notNull(),
});
export const rates = pgTable(
"rates",
{
userId: varchar("userId", { length: 256 }).notNull(),
commentId: integer("commentId").notNull(),
like: boolean("like").notNull(),
},
(table) => [
primaryKey({ columns: [table.userId, table.commentId] }),
index("comment_idx").on(table.commentId),
],
);
import { createDrizzleAdapter } from "@fuma-comment/server/adapters/drizzle";
import { comments, rates, roles, user } from "@/lib/schema";
const storage = createDrizzleAdapter({
db,
schemas: {
comments,
rates,
roles,
user,
},
// Use tables created by your auth provider
auth: "next-auth" | "better-auth",
});
Auth Provider
Fuma Comment supports multiple auth providers, you can choose one of them:
import { createNextAuthAdapter } from "@fuma-comment/server/adapters/next-auth";
// Next Auth options
import { authOptions } from "@/app/api/auth/[...nextauth]/options";
const auth = createNextAuthAdapter(authOptions);
Server
Fuma Comment supports several web/backend frameworks to host the backend server.
Next.js
Create app/api/comments/[[...comment]]/route.ts
.
import { NextComment } from "@fuma-comment/server/next";
export const { GET, DELETE, PATCH, POST } = NextComment({
// import from comment.config.ts
auth,
storage,
});
Express
Pass your express app as a parameter, all the endpoints will be added under /api/comments
.
import { ExpressComment } from "@fuma-comment/server/express";
ExpressComment({
// your app
app,
// import from comment.config.ts
auth
storage
});
Elysia
import Elysia from "elysia";
import { commentPlugin } from "@fuma-comment/server/elysia";
const app = new Elysia()
.use(
commentPlugin({
// pass your adapters
auth,
storage,
elysia: {
// or other prefix if you wanted
prefix: "/api/comments",
},
})
)
.listen(8080);
const res = await fetch("http://localhost:8080/api/comments/page");
console.log(await res.json());
Done!
You can now have fun with Fuma Comment!