This November we’ve expanded Text Editor and made some UX improvements.

- [Official Tiptap support](#official-tiptap-support) - New Text Editor package
  adding support for Tiptap.
- [Formatting toolbar for Comments](#formatting-toolbar-for-comments) - Floating
  composer options for text styles.
- [Preventing unsaved changes](#preventing-unsaved-changes) - Two new ways to
  stop unsynchronized changes.
- [Permission info in dashboard](#permission-info-in-dashboard) - Check if your
  rooms are public or private.

## Upgrade now

To use the latest features, update your packages with the following command.

```bash
npx create-liveblocks-app@latest --upgrade
```

If you were previously on Liveblocks 2.8 or below, make sure to follow our
[upgrade guides](/docs/platform/upgrading) before updating.

## Official Tiptap support [#official-tiptap-support]

It’s now easier than ever to set up collaborative text documents using our new
first-party package for [Tiptap](/docs/products/text-editor/tiptap), a highly
extensible rich-text editor. Text documents are fully-hosted and stored in our
highly scalable realtime data store, meaning you just need to import our
component to [get started](/docs/get-started/text-editor/tiptap).

<Figure>
  <video autoPlay loop muted playsInline>
    <source
      src="/images/blog/whats-new-in-liveblocks-november-24/tiptap-launch-overview.mp4"
      type="video/mp4"
    />
  </video>
</Figure>

Our new [Text Editor](/text-editor) package makes it easy to create an advanced
collaborative editor filled with all the features you’ve come to expect from
Liveblocks, such as:

- [Collaborative editing](/blog/easy-collaborative-text-editing-with-liveblocks-tiptap#collaborative-editing) -
  Modify your document in realtime with others.
- [Commenting](/blog/easy-collaborative-text-editing-with-liveblocks-tiptap#commenting) -
  Annotate text and leave fully-featured threaded comments.
- [Notifications](/blog/easy-collaborative-text-editing-with-liveblocks-tiptap#notifications) -
  Display in-app notifications when users are mentioned.
- [Instant loading](/blog/easy-collaborative-text-editing-with-liveblocks-tiptap#instant-loading) -
  With offline support, no loading spinners are necessary.
- [Multiple editors](/blog/easy-collaborative-text-editing-with-liveblocks-tiptap#multiple-editors) -
  Add a number of text editors to one document.
- [AI suggestions](/blog/easy-collaborative-text-editing-with-liveblocks-tiptap#ai-suggestions) -
  Learn how to add AI suggestions with our Novel example.
- [Fully customizable](/blog/easy-collaborative-text-editing-with-liveblocks-tiptap#fully-customizable) -
  Integrate your editor perfectly into any design.

### Learn more in our blog post

To learn more, make sure to read our previous blog post:
[Easy collaborative text editing with Liveblocks Tiptap](/blog/easy-collaborative-text-editing-with-liveblocks-tiptap).

<UniversalLink href="/blog/easy-collaborative-text-editing-with-liveblocks-tiptap">
  <Figure>
    <Image
      src="/images/blog/social-images/easy-collaborative-text-editing-with-liveblocks-tiptap.jpg"
      alt="Liveblocks Tiptap blog post"
      width={750}
      height={394}
      quality={95}
    />
  </Figure>
</UniversalLink>

## Formatting toolbar for Comments [#formatting-toolbar-for-comments]

When composing comments, a contextual formatting toolbar is now shown when
selecting text, allowing your users to easily add styles to their comments. Four
styles are supported: <span className="font-bold">Bold</span>, _italic_,
~strikethrough~ and `code`.

<Figure>
  <video autoPlay loop muted playsInline>
    <source
      src="/images/blog/whats-new-in-liveblocks-november-24/floating-composer-toolbar.mp4"
      type="video/mp4"
    />
  </video>
</Figure>

After updating to Liveblocks 2.13, the
[Composer’s](/docs/api-reference/liveblocks-react-ui#Composer-props) formatting
toolbar is enabled by default, but you can optionally disable it with
`showFormattingControls={false}`.

```tsx
function Component() {
  return <Composer showFormattingControls={false} />;
}
```

### Primitives toolbar

If you’ve built a custom composer using
[Comments primitives](/docs/products/comments/primitives), new
[`FloatingToolbar`](/docs/api-reference/liveblocks-react-ui#primitives-Composer.FloatingToolbar)
and [`MarkToggle`](/docs/api-reference/liveblocks-react-ui#Composer.MarkToggle)
components allow you to create a custom floating toolbar for your app.

```tsx highlight="5-18"
<Composer.Form>
  <Composer.Editor
    components={{
      FloatingToolbar: () => (
        <Composer.FloatingToolbar className="border bg-white">
          <Composer.MarkToggle
            mark="bold"
            className="bg-gray-100 aria-pressed:bg-gray-300"
          >
            Bold
          </Composer.MarkToggle>
          <Composer.MarkToggle
            mark="italic"
            className="bg-gray-100 aria-pressed:bg-gray-300"
          >
            Italic
          </Composer.MarkToggle>
        </Composer.FloatingToolbar>
      ),
      // ...
    }}
  />
</Composer.Form>
```

## Preventing unsaved changes [#preventing-unsaved-changes]

Liveblocks synchronizes very quickly, but occasionally, particularly when a user
has a poor connection, they will close a browser tab before a change is saved.
This could lead to issues such as a reaction not appearing on a comment, a word
in a text editor not saving, a thread not being resolved.

To combat this, we’ve come up with two different solutions.

- [useSyncStatus](#use-sync-status) - A new way to show a synchronization badge
  in your UI.
- [preventUnsavedChanges](#prevent-unsaved-changes) - Stop users closing tabs
  while unsynchronized.

### useSyncStatus [#use-sync-status]

It’s now easier than ever to create a synchronization badge in your app. We’ve
added a new hook which checks every part of Liveblocks (Comments, Text Editor,
Notifications, Storage, Yjs) and lets you know whether changes have been saved.
You can use this information to create a status badge, such as the one in our
[Linear-like issue tracker example](/examples/linear-like-issue-tracker).

<Figure>
  <video autoPlay loop muted playsInline>
    <source
      src="/images/blog/whats-new-in-liveblocks-november-24/use-sync-status.mp4"
      type="video/mp4"
    />
  </video>
</Figure>

The new hook’s called
[`useSyncStatus`](/docs/api-reference/liveblocks-react#useSyncStatus) and it’s
just a few lines of code to create a badge.

```tsx
import { useSyncStatus } from "@liveblocks/react/suspense";

function StatusBadge() {
  const syncStatus = useSyncStatus({ smooth: true });

  return <div>{syncStatus === "synchronized" ? "✅ Saved" : "🔄 Saving"}</div>;
}
```

Because Liveblocks syncs quickly and often, ordinarily the badge would flash a
lot. To account for this, we’ve added a `smooth` option, which delays changes
between states, so your UI isn't distracting. We’re using this in the video
above.

### preventUnsavedChanges [#prevent-unsaved-changes]

We’ve added a new option that prevent users from closing tabs with a dialog if
there are any unsaved changes. Along with preventing unsynchronized changes in
every part of Liveblocks, it additionally prevents users from closing the page
when they’ve left unsubmitted text in a
[Composer](/docs/api-reference/liveblocks-react-ui#Composer).

<Figure>
  <Image
    src="/images/blog/whats-new-in-liveblocks-november-24/unsynched-changes.png"
    alt="Prevent unsaved changes dialog"
    width={1512}
    height={966}
    quality={95}
  />
</Figure>

To enable this feature, use the
[`preventUnsavedChanges`](/docs/api-reference/liveblocks-react#prevent-users-losing-unsaved-changes)
option in `LiveblocksProvider`.

```tsx highlight="4"
function Page() {
  return (
    <LiveblocksProvider
      preventUnsavedChanges={true}
      // ...
    >
      ...
    </LiveblocksProvider>
  );
}
```

## Permission info in dashboard [#permission-info-in-dashboard]

You can now quickly check if your rooms are public or private on our
[dashboard](/dashboard), and we’ve added a warning that lets you know if all
your rooms are public, to let you know if you’re accidentally exposing your room
data.

<Figure>
  <video autoPlay loop muted playsInline>
    <source
      src="/images/blog/whats-new-in-liveblocks-november-24/public-private-dashboard.mp4"
      type="video/mp4"
    />
  </video>
</Figure>

### Why your rooms might be public

If you’re not creating rooms with our
[Node.js package](/docs/api-reference/liveblocks-node#post-rooms) or
[REST API](/docs/api-reference/rest-api-endpoints#post-rooms), this means we’ve
automatically creating rooms for you. Each room created automatically is set to
have public permissions, which means that users don’t need authentication to
access the room.

To only allow authenticated users, you can easily
[change permissions on a room](/docs/api-reference/liveblocks-node#post-rooms-roomId),
or
[create a room with private access](/docs/api-reference/liveblocks-node#post-rooms)
right from the start.

```tsx
// Turn an existing room private
const otherRoom = await liveblocks.updateRoom("my-other-room-id", {
  defaultAccesses: [], // Private access
});

// Create a new private room
const room = await liveblocks.createRoom("my-room-id", {
  defaultAccesses: [], // Private access
});
```

Rooms are only automatically created if you’re using access token
authentication, as ID tokens always require you to manually create rooms. Learn
more about
[setting default room permissions with access tokens](/docs/authentication/access-token#Default-permissions).

## Upgrade

To use the latest features, update your packages with the following command.

```bash
npx create-liveblocks-app@latest --upgrade
```

If you were previously on Liveblocks 2.8 or below, make sure to follow our
[upgrade guides](/docs/platform/upgrading) before updating.

## Contributors

<Contributors
  gitHubUsernames={[
    "haydenbleasel",
    "ctnicholas",
    "pierrelevaillant",
    "nvie",
    "nimeshnayaju",
    "sugardarius",
    "marcbouchenoire",
    "jrowny",
  ]}
/>