Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions CHANGELOG_PUBLIC.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,56 @@ list and feel free to give them credit at the end of a line, e.g.:

-->

# Week 10 (2026-03-06)

## v3.15.0

### `@liveblocks/react-ui`

- Add various new ways to customize `Thread` and `Comment`:
- Comments in `Thread` can now be overridden or customized via the
`components` prop.
- New parts of `Comment` (content, avatar, author, and date) can now be
overridden or customized via the `children`, `additionalContent`, `avatar`,
`author`, and `date` props.
- Fix `commentDropdownItems` prop on `Thread` not working as expected in some
cases.

### `@liveblocks/react`

- Each `createRoomContext()` invocation now creates its own isolated context to
allow nesting independent room contexts and their `RoomProvider` components.

### `@liveblocks/react-blocknote`

- Support newer BlockNote versions and bump the minimum required version to
v0.43.0. (Thanks @nperez0111 for the contribution!)

### `@liveblocks/react-ui`, `@liveblocks/react-tiptap`, and `@liveblocks/react-lexical`

- Improve how inline components passed to `components={{ ... }}` props are
handled by keeping them stable instead of re-mounting them on every render.
- Move `@radix-ui/*` dependencies to the `radix-ui` mono package.

## Examples

- New example: [AG Grid Comments](https://liveblocks.io/examples/ag-grid-comments/nextjs-comments-ag-grid).
- Update old examples to use new presence and commenting components.

## Documentation

- New quickstart: [Draggable comments with Next.js](https://liveblocks.io/docs/get-started/nextjs-comments-canvas).
- New quickstart: [Commenting inside AG Grid with Next.js](https://liveblocks.io/docs/get-started/nextjs-comments-ag-grid).
- New quickstart: [Commenting inside a table with Next.js](https://liveblocks.io/docs/get-started/nextjs-comments-table).
- New quickstart: [Realtime avatar and cursor presence with Next.js](https://liveblocks.io/docs/get-started/nextjs-presence).
- New guide: [How to add users to Liveblocks presence components](https://liveblocks.io/docs/guides/how-to-add-users-to-liveblocks-presence-components).
- Mention `sk_localdev` and `pk_localdev` keys more explicitly in dev server docs.
- Mention `["comments:write"]` permission under authentication.

## Contributors

nperez0111, marcbouchenoire, ctnicholas

# Week 9 (2026-02-27)

## v3.14.1
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/get-started/nextjs-presence.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ the [`@liveblocks/react`](/docs/api-reference/liveblocks-react) package.
add each user's name and avatar.

<Button asChild className="not-markdown">
<a href="/docs/authentication">
<a href="/docs/guides/how-to-add-users-to-liveblocks-presence-components">
Set up authentication and add user information
</a>
</Button>
Expand Down
59 changes: 34 additions & 25 deletions examples/nextjs-comments-ag-grid/app/CommentCell.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,30 @@
"use client";

import {
Comment,
CommentPin,
FloatingComposer,
FloatingThread,
Icon,
} from "@liveblocks/react-ui";
import { useSelf } from "@liveblocks/react";
import { CustomCellRendererProps } from "ag-grid-react";
import { useCellThread } from "./CellThreadContext";
import { CSSProperties, useState } from "react";

const COMMENT_PIN_SIZE = 24;

const commentPinStyle = {
"--lb-comment-pin-padding": "3px",
width: COMMENT_PIN_SIZE,
height: COMMENT_PIN_SIZE,
cursor: "pointer",
marginTop: 3,
} as CSSProperties;

export function CommentCell(params: CustomCellRendererProps) {
const { threads, openCell, setOpenCell } = useCellThread();
const currentUserId = useSelf((self) => self.id) ?? undefined;
const [isComposerOpen, setIsComposerOpen] = useState(false);

const rowId = params.data?.id;
const columnId = params.colDef?.field;
Expand Down Expand Up @@ -37,35 +52,33 @@ export function CommentCell(params: CustomCellRendererProps) {
style={{
display: "flex",
alignItems: "center",
justifyContent: "space-between",
gap: 16,
gap: 12,
}}
>
{/* Cell contents */}
{params.value}

{/* Show thread if it exists, otherwise show thread composer (plus on hover) */}
{!thread ? (
<div className="comment-cell-trigger">
<div
className="comment-cell-trigger"
data-open={isComposerOpen || undefined}
>
<FloatingComposer
metadata={metadata}
onComposerSubmit={() => setOpenCell(metadata)}
onOpenChange={setIsComposerOpen}
style={{ zIndex: 10 }}
>
<button>
<svg
width={16}
height={16}
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth={2}
strokeLinecap="round"
strokeLinejoin="round"
>
<path d="M2.992 16.342a2 2 0 01.094 1.167l-1.065 3.29a1 1 0 001.236 1.168l3.413-.998a2 2 0 011.099.092 10 10 0 10-4.777-4.719M8 12h8M12 8v8" />
</svg>
</button>
<CommentPin
corner="top-left"
style={commentPinStyle}
userId={currentUserId}
>
{!isComposerOpen ? (
<Icon.Plus style={{ width: 14, height: 14 }} />
) : null}
</CommentPin>
</FloatingComposer>
</div>
) : (
Expand All @@ -81,13 +94,9 @@ export function CommentCell(params: CustomCellRendererProps) {
style={{ zIndex: 10 }}
autoFocus
>
<Comment.Avatar
style={{
width: 28,
height: 28,
borderRadius: "100%",
cursor: "pointer",
}}
<CommentPin
corner="top-left"
style={commentPinStyle}
userId={thread.comments[0]?.userId}
/>
</FloatingThread>
Expand Down
20 changes: 3 additions & 17 deletions examples/nextjs-comments-ag-grid/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -34,30 +34,16 @@ button {
--lb-accent: var(--ag-accent-color, #2196f3);
}

/* Comment cell: plus trigger is hidden until row/cell is hovered */
/* Hover to show "+" CommentPin icon */
.comment-cell-trigger {
opacity: 0;
transition: opacity 0.15s ease;
}
.comment-cell:hover .comment-cell-trigger {
.comment-cell:hover .comment-cell-trigger,
.comment-cell-trigger[data-open] {
opacity: 1;
}

/* Style the plus (add comment) trigger here */
.comment-cell-trigger button {
border-radius: 100%;
background: #ddf0ff;
padding-top: 0px;
appearance: none;
height: 28px;
width: 28px;
border: 0;
display: flex;
align-items: center;
justify-content: center;
color: #111c26;
}

.lb-portal {
z-index: 10;
}
52 changes: 26 additions & 26 deletions examples/nextjs-comments-ag-grid/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions examples/nextjs-comments-ag-grid/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
"start": "next start"
},
"dependencies": {
"@liveblocks/client": "^3.15.0-rc1",
"@liveblocks/node": "^3.15.0-rc1",
"@liveblocks/react": "^3.15.0-rc1",
"@liveblocks/react-ui": "^3.15.0-rc1",
"@liveblocks/client": "^3.15.1-rc1",
"@liveblocks/node": "^3.15.1-rc1",
"@liveblocks/react": "^3.15.1-rc1",
"@liveblocks/react-ui": "^3.15.1-rc1",
"ag-grid-community": "^35.1.0",
"ag-grid-react": "^35.1.0",
"next": "^16.1.6",
Expand Down
4 changes: 2 additions & 2 deletions packages/liveblocks-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@
"format": "(eslint --fix src/ e2e/ || true) && prettier --write src/ e2e/",
"lint": "eslint src/",
"lint:package": "publint --strict && attw --pack",
"test": "NODE_OPTIONS=\"--no-deprecation\" vitest run",
"test:ci": "NODE_OPTIONS=\"--no-deprecation\" vitest run",
"test": "npx liveblocks dev -c 'vitest run --coverage'",
"test:ci": "vitest run",
"test:types": "ls test-d/* | xargs -n1 tsd --files",
"test:watch": "NODE_OPTIONS=\"--no-deprecation\" vitest",
"test:e2e": "npx liveblocks dev -p 1154 -c 'vitest run --config=./vitest.config.e2e.ts'",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ import { ServerMsgCode } from "../protocol/ServerMsg";
import type { WebsocketCloseCodes } from "../types/IWebSocket";
import type { MockWebSocket } from "./_MockWebSocketServer";
import { MockWebSocketServer } from "./_MockWebSocketServer";
import { makeAccessToken, makeIDToken, serverMessage } from "./_utils";
import {
makeAccessToken,
makeIDToken,
serverMessage,
} from "./_MockWebSocketServer.setup";

type AuthBehavior = () => AuthValue;
type SocketBehavior = (wss: MockWebSocketServer) => MockWebSocket;
Expand Down
Loading
Loading