diff --git a/.github/assets/examples/comments-private.png b/.github/assets/examples/comments-private.png
new file mode 100644
index 00000000000..523fcd510c4
Binary files /dev/null and b/.github/assets/examples/comments-private.png differ
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b6dc1764c4b..9b99370f1b0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,41 @@
## vNEXT (not yet released)
+## v3.21.0
+
+### All packages
+
+- Add support for public and private threads. Threads now have a `visibility`
+ property that is `"public"` by default but can be set to `"private"` when
+ created. Permissions can be used to decide which threads a user has access to,
+ and threads can also be queried by their visibility to create filtered views.
+- Add scoped comments permissions such as `comments:public:write` and
+ `comments:private:none`.
+
+### `@liveblocks/client`
+
+- **Breaking:** Remove `type` and `kind` fields from `HistoryVersion` type. The
+ backend no longer returns these.
+- Add `visibility` to `createThread`.
+- Support querying by `visibility` in `getThreads`.
+
+### `@liveblocks/react`
+
+- Add `visibility` to `useCreateThread`.
+- Support querying by `visibility` in `useThreads`.
+- Add `useHistoryVersionYjsData()` hook to retrieve raw Yjs binary data for a
+ given version. Deprecate `useHistoryVersionData()` in its favor.
+
+### `@liveblocks/node`
+
+- Add `visibility` to `createThread`.
+- Support querying by `visibility` in `getThreads`.
+
+### `@liveblocks/react-ui`
+
+- Add a `visibility` prop to `Composer`.
+- Prevent `Composer` from collapsing after focusing and blurring unless it was
+ explicitly meant to support a collapsed state.
+
## v3.20.1
### `@liveblocks/client`
diff --git a/docs/pages/api-reference/liveblocks-client.mdx b/docs/pages/api-reference/liveblocks-client.mdx
index b69faf47bad..76879bd9a34 100644
--- a/docs/pages/api-reference/liveblocks-client.mdx
+++ b/docs/pages/api-reference/liveblocks-client.mdx
@@ -2914,6 +2914,11 @@ console.log(inboxNotifications);
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
-
+
-
-
-
-
-
-
-
+
-
-
diff --git a/examples/nextjs-ai-dashboard-reports/README.md b/examples/nextjs-ai-dashboard-reports/README.md
index 0cebb5e6f25..8a9cc235808 100644
--- a/examples/nextjs-ai-dashboard-reports/README.md
+++ b/examples/nextjs-ai-dashboard-reports/README.md
@@ -10,7 +10,7 @@
# AI chat in reports dashboard
diff --git a/examples/nextjs-ai-elements-realtime/README.md b/examples/nextjs-ai-elements-realtime/README.md
index 4a4a26850c1..b8f34be90f3 100644
--- a/examples/nextjs-ai-elements-realtime/README.md
+++ b/examples/nextjs-ai-elements-realtime/README.md
@@ -10,7 +10,7 @@
# Realtime AI chat with AI Elements
diff --git a/examples/nextjs-ai-elements-realtime/components/HelpButton.tsx b/examples/nextjs-ai-elements-realtime/components/HelpButton.tsx
index 4e796010568..65dcf39b8e6 100644
--- a/examples/nextjs-ai-elements-realtime/components/HelpButton.tsx
+++ b/examples/nextjs-ai-elements-realtime/components/HelpButton.tsx
@@ -6,7 +6,7 @@ import { Button } from "./ui/button";
const EXAMPLE_NAME = "Realtime AI chat with AI Elements";
const EXAMPLE_URL =
- "https://liveblocks.io/examples/nextjs-ai-elements-realtime";
+ "https://liveblocks.io/examples/ai-elements-realtime/nextjs-ai-elements-realtime";
type Feature = {
icon: ReactNode;
diff --git a/examples/nextjs-ai-spreadsheet/README.md b/examples/nextjs-ai-spreadsheet/README.md
index 3a5fd776a52..3cb4c224e98 100644
--- a/examples/nextjs-ai-spreadsheet/README.md
+++ b/examples/nextjs-ai-spreadsheet/README.md
@@ -10,7 +10,7 @@
# Realtime AI spreadsheet
diff --git a/examples/nextjs-ai-spreadsheet/components/HelpButton.tsx b/examples/nextjs-ai-spreadsheet/components/HelpButton.tsx
index d38de069aaf..242ab8ce9fa 100644
--- a/examples/nextjs-ai-spreadsheet/components/HelpButton.tsx
+++ b/examples/nextjs-ai-spreadsheet/components/HelpButton.tsx
@@ -5,7 +5,8 @@ import { createPortal } from "react-dom";
import { Button } from "./ui/button";
const EXAMPLE_NAME = "Realtime AI spreadsheet";
-const EXAMPLE_URL = "https://liveblocks.io/examples/nextjs-ai-spreadsheet";
+const EXAMPLE_URL =
+ "https://liveblocks.io/examples/ai-spreadsheet/nextjs-ai-spreadsheet";
type Feature = {
icon: ReactNode;
diff --git a/examples/nextjs-comments-ai/README.md b/examples/nextjs-comments-ai/README.md
index 69703b581c0..24c032c9d22 100644
--- a/examples/nextjs-comments-ai/README.md
+++ b/examples/nextjs-comments-ai/README.md
@@ -10,7 +10,7 @@
# Comments with AI replies
diff --git a/examples/nextjs-comments-audio/README.md b/examples/nextjs-comments-audio/README.md
index fc52055f184..fabe594fc62 100644
--- a/examples/nextjs-comments-audio/README.md
+++ b/examples/nextjs-comments-audio/README.md
@@ -10,7 +10,7 @@
# Audio Comments
diff --git a/examples/nextjs-comments-canvas/README.md b/examples/nextjs-comments-canvas/README.md
index 4c33b750017..b9d945b73f3 100644
--- a/examples/nextjs-comments-canvas/README.md
+++ b/examples/nextjs-comments-canvas/README.md
@@ -10,7 +10,7 @@
# Canvas Comments
diff --git a/examples/nextjs-comments-emails-resend/README.md b/examples/nextjs-comments-emails-resend/README.md
index a132b4625dc..d52f9c06f38 100644
--- a/examples/nextjs-comments-emails-resend/README.md
+++ b/examples/nextjs-comments-emails-resend/README.md
@@ -10,7 +10,7 @@
# Comments Notification Emails (Resend)
diff --git a/examples/nextjs-comments-emails-sendgrid/README.md b/examples/nextjs-comments-emails-sendgrid/README.md
index b546848db2b..adfbe95c6c3 100644
--- a/examples/nextjs-comments-emails-sendgrid/README.md
+++ b/examples/nextjs-comments-emails-sendgrid/README.md
@@ -10,7 +10,7 @@
# Comments Notification Emails (SendGrid)
diff --git a/examples/nextjs-comments-notifications/README.md b/examples/nextjs-comments-notifications/README.md
index 9436433bea5..936428181f7 100644
--- a/examples/nextjs-comments-notifications/README.md
+++ b/examples/nextjs-comments-notifications/README.md
@@ -10,7 +10,7 @@
# Comments Notifications
diff --git a/examples/nextjs-comments-overlay/README.md b/examples/nextjs-comments-overlay/README.md
index 3d35034143a..8ed39e2689e 100644
--- a/examples/nextjs-comments-overlay/README.md
+++ b/examples/nextjs-comments-overlay/README.md
@@ -10,7 +10,7 @@
# Overlay Comments
diff --git a/examples/nextjs-comments-primitives/README.md b/examples/nextjs-comments-primitives/README.md
index b8627037b69..b851ecd8760 100644
--- a/examples/nextjs-comments-primitives/README.md
+++ b/examples/nextjs-comments-primitives/README.md
@@ -10,7 +10,7 @@
# Comments Primitives
diff --git a/examples/nextjs-comments-private/.env.example b/examples/nextjs-comments-private/.env.example
new file mode 100644
index 00000000000..9c176850c85
--- /dev/null
+++ b/examples/nextjs-comments-private/.env.example
@@ -0,0 +1,2 @@
+# https://liveblocks.io/dashboard/apikeys
+LIVEBLOCKS_SECRET_KEY=
diff --git a/examples/nextjs-comments-private/.gitignore b/examples/nextjs-comments-private/.gitignore
new file mode 100644
index 00000000000..3a68e0cfc9d
--- /dev/null
+++ b/examples/nextjs-comments-private/.gitignore
@@ -0,0 +1,12 @@
+.DS_Store
+node_modules
+.env
+.env.*
+!.env.example
+*.tsbuildinfo
+.vercel
+.next
+out
+next-env.d.ts
+# Turborepo
+.turbo
diff --git a/examples/nextjs-comments-private/.prettierrc b/examples/nextjs-comments-private/.prettierrc
new file mode 100644
index 00000000000..06998724304
--- /dev/null
+++ b/examples/nextjs-comments-private/.prettierrc
@@ -0,0 +1,11 @@
+{
+ "semi": true,
+ "tabWidth": 2,
+ "useTabs": false,
+ "singleQuote": false,
+ "jsxSingleQuote": false,
+ "arrowParens": "always",
+ "bracketSpacing": true,
+ "bracketSameLine": false,
+ "trailingComma": "es5"
+}
diff --git a/examples/nextjs-comments-private/README.md b/examples/nextjs-comments-private/README.md
new file mode 100644
index 00000000000..5e424b6e82c
--- /dev/null
+++ b/examples/nextjs-comments-private/README.md
@@ -0,0 +1,87 @@
+
+
+# Private Commenting
+
+
+
+This example shows how to add private commenting to your app with
+[Liveblocks](https://liveblocks.io) and [Next.js](https://nextjs.org/).
+
+
+
+## Getting started
+
+Run the following command to try this example locally:
+
+```bash
+npx create-liveblocks-app@latest --example nextjs-comments-private --api-key
+```
+
+This will download the example and ask permission to open your browser, enabling
+you to automatically get your API key from your
+[liveblocks.io](https://liveblocks.io) account.
+
+### Manual setup
+
+Read more
+
+
+
+Alternatively, you can set up your project manually:
+
+- Install all dependencies with `npm install`
+- Create an account on [liveblocks.io](https://liveblocks.io/dashboard)
+- Copy your **secret** key from the
+ [dashboard](https://liveblocks.io/dashboard/apikeys)
+- Create an `.env.local` file and add your **secret** key as the
+ `LIVEBLOCKS_SECRET_KEY` environment variable
+- Run `npm run dev` and go to [http://localhost:3000](http://localhost:3000)
+
+Read more
+
+
+
+To both deploy on [Vercel](https://vercel.com), and run the example locally, use
+the following command:
+
+```bash
+npx create-liveblocks-app@latest --example nextjs-comments-private --vercel
+```
+
+This will download the example and ask permission to open your browser, enabling
+you to deploy to Vercel.
+
+Read more
+
+
+
+After forking
+[this example](https://codesandbox.io/s/github/liveblocks/liveblocks/tree/main/examples/nextjs-comments-private)
+on CodeSandbox, create the `LIVEBLOCKS_SECRET_KEY` environment variable as a
+[secret](https://codesandbox.io/docs/secrets).
+
+
+
+
diff --git a/examples/nextjs-comments-tiptap/README.md b/examples/nextjs-comments-tiptap/README.md
index 876dc3c8120..fc76f478106 100644
--- a/examples/nextjs-comments-tiptap/README.md
+++ b/examples/nextjs-comments-tiptap/README.md
@@ -10,7 +10,7 @@
# Text Editor Comments (Tiptap)
diff --git a/examples/nextjs-comments-video/README.md b/examples/nextjs-comments-video/README.md
index 18c11ca5927..c1bfcd4bd6e 100644
--- a/examples/nextjs-comments-video/README.md
+++ b/examples/nextjs-comments-video/README.md
@@ -10,7 +10,7 @@
# Video Comments
diff --git a/examples/nextjs-lexical-emails-resend/README.md b/examples/nextjs-lexical-emails-resend/README.md
index 9548559464e..f52a898693b 100644
--- a/examples/nextjs-lexical-emails-resend/README.md
+++ b/examples/nextjs-lexical-emails-resend/README.md
@@ -10,7 +10,7 @@
# Lexical Notification Emails (Resend)
diff --git a/examples/nextjs-lexical/README.md b/examples/nextjs-lexical/README.md
index f8c882ecc79..e40d03c24c5 100644
--- a/examples/nextjs-lexical/README.md
+++ b/examples/nextjs-lexical/README.md
@@ -10,7 +10,7 @@
# Collaborative Rich Text Editor (Lexical)
diff --git a/examples/nextjs-multiplayer-handsontable/README.md b/examples/nextjs-multiplayer-handsontable/README.md
index 270965e531f..7a8adc280e9 100644
--- a/examples/nextjs-multiplayer-handsontable/README.md
+++ b/examples/nextjs-multiplayer-handsontable/README.md
@@ -10,7 +10,7 @@
# Multiplayer Handsontable
diff --git a/examples/nextjs-notifications-custom/README.md b/examples/nextjs-notifications-custom/README.md
index 3fcddd205e9..c578a3f53ee 100644
--- a/examples/nextjs-notifications-custom/README.md
+++ b/examples/nextjs-notifications-custom/README.md
@@ -10,7 +10,7 @@
# Custom Notifications
diff --git a/examples/nextjs-react-flow-ai/README.md b/examples/nextjs-react-flow-ai/README.md
index 5324e6f9803..55443df35d3 100644
--- a/examples/nextjs-react-flow-ai/README.md
+++ b/examples/nextjs-react-flow-ai/README.md
@@ -10,7 +10,7 @@
# Collaborative React Flow with AI
diff --git a/examples/nextjs-tiptap-ai/README.md b/examples/nextjs-tiptap-ai/README.md
index a1cdc62013c..1915856152a 100644
--- a/examples/nextjs-tiptap-ai/README.md
+++ b/examples/nextjs-tiptap-ai/README.md
@@ -10,9 +10,6 @@
# Collaborative Rich Text Editor with AI (Tiptap)
-
diff --git a/examples/nextjs-tiptap-emails-resend/README.md b/examples/nextjs-tiptap-emails-resend/README.md
index fe56549a5ae..909edf5f913 100644
--- a/examples/nextjs-tiptap-emails-resend/README.md
+++ b/examples/nextjs-tiptap-emails-resend/README.md
@@ -10,7 +10,7 @@
# TipTap Notifications Emails (Resend)
diff --git a/examples/nextjs-yjs-blocknote-advanced/README.md b/examples/nextjs-yjs-blocknote-advanced/README.md
index 4b73d148d74..1adb8120496 100644
--- a/examples/nextjs-yjs-blocknote-advanced/README.md
+++ b/examples/nextjs-yjs-blocknote-advanced/README.md
@@ -10,9 +10,6 @@
# Advanced Collaborative Rich Text Editor (BlockNote)
-
diff --git a/examples/nextjs-yjs-blocknote/README.md b/examples/nextjs-yjs-blocknote/README.md
index 9d9edc55acd..a066bf6e3a1 100644
--- a/examples/nextjs-yjs-blocknote/README.md
+++ b/examples/nextjs-yjs-blocknote/README.md
@@ -10,9 +10,6 @@
# Collaborative Rich Text Editor (BlockNote)
-
diff --git a/examples/nextjs-yjs-lexical/README.md b/examples/nextjs-yjs-lexical/README.md
index ca79a3d7bb3..ceabcde6895 100644
--- a/examples/nextjs-yjs-lexical/README.md
+++ b/examples/nextjs-yjs-lexical/README.md
@@ -10,9 +10,6 @@
# Collaborative Rich Text Editor (Lexical)
-
diff --git a/examples/nextjs-yjs-monaco/README.md b/examples/nextjs-yjs-monaco/README.md
index 20ef9defcd0..7e8cc935aa3 100644
--- a/examples/nextjs-yjs-monaco/README.md
+++ b/examples/nextjs-yjs-monaco/README.md
@@ -10,7 +10,7 @@
# Collaborative Code Editor (Monaco)
diff --git a/examples/nextjs-yjs-tiptap/README.md b/examples/nextjs-yjs-tiptap/README.md
index 0eaccfd1022..7c089abc95f 100644
--- a/examples/nextjs-yjs-tiptap/README.md
+++ b/examples/nextjs-yjs-tiptap/README.md
@@ -10,9 +10,6 @@
# Collaborative Rich Text Editor (Tiptap)
-
diff --git a/guides/guides.json b/guides/guides.json
index c7580046415..fa37bb26011 100644
--- a/guides/guides.json
+++ b/guides/guides.json
@@ -511,5 +511,19 @@
"topics": ["presence", "tutorials", "data-fetching"],
"technologies": ["react-ui", "react"],
"date": "2026-03-04"
+ },
+ {
+ "title": "How to use public and private threads",
+ "path": "/how-to-use-public-and-private-threads",
+ "topics": ["comments", "authentication"],
+ "technologies": ["react", "nodejs"],
+ "date": "2026-06-23"
+ },
+ {
+ "title": "How to add private commenting to your app",
+ "path": "/how-to-add-private-commenting-to-your-app",
+ "topics": ["comments", "authentication"],
+ "technologies": ["react", "nextjs"],
+ "date": "2026-06-24"
}
]
diff --git a/guides/pages/how-to-add-private-commenting-to-your-app.mdx b/guides/pages/how-to-add-private-commenting-to-your-app.mdx
new file mode 100644
index 00000000000..d97e95b3783
--- /dev/null
+++ b/guides/pages/how-to-add-private-commenting-to-your-app.mdx
@@ -0,0 +1,195 @@
+---
+meta:
+ title: "How to add private commenting to your app"
+ description:
+ "Learn how to add private comments for admins and team members, alongside
+ your normal public comments."
+---
+
+Some apps have comments that only certain people should see—internal notes,
+moderation discussions, or team-only annotations. With Liveblocks Comments you
+can add these alongside your normal public comments, for example with a separate
+“Leave note” button next to the regular comment button.
+
+
&
@@ -1466,14 +1482,6 @@ export type RoomContextBundle<
*/
useHistoryVersions(): HistoryVersionsAsyncSuccess;
- // /**
- // * Returns the data of a specific version of the current room's history.
- // *
- // * @example
- // * const { data } = useHistoryVersionData(version.id);
- // */
- // useHistoryVersionData(versionId: string): HistoryVersionDataState;
-
/**
* Returns the user's subscription settings for the current room
* and a function to update them.
diff --git a/packages/liveblocks-react/src/umbrella-store.ts b/packages/liveblocks-react/src/umbrella-store.ts
index 652c49b5257..4c181158e95 100644
--- a/packages/liveblocks-react/src/umbrella-store.ts
+++ b/packages/liveblocks-react/src/umbrella-store.ts
@@ -1672,7 +1672,7 @@ export class UmbrellaStore