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:

lib/schema.ts
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),
	],
);
lib/comment.config.ts
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:

lib/comment.config.ts
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.

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!