npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dittolive/ditto-chat-core

v0.1.4

Published

`@dittolive/ditto-chat-core` is a React and TypeScript-based library leveraging Ditto for real-time chat functionalities.

Readme

dittochatcore

@dittolive/ditto-chat-core is a React and TypeScript-based library leveraging Ditto for real-time chat functionalities.

Table of Contents

Installation

You can install @dittolive/ditto-chat-core using npm or yarn:

npm install @dittolive/ditto-chat-core
# or
yarn add @dittolive/ditto-chat-core

Usage

Here's a basic example of how to use @dittolive/ditto-chat-core in your React application:

import React, { useEffect, useState } from 'react';
import { Ditto } from '@dittolive/ditto';
import { useDittoChat, useDittoChatStore } from '@dittolive/ditto-chat-core';

// Assume you have a Ditto instance and user details
const dittoInstance: Ditto | null = null; // Your Ditto instance
const currentUserId = 'your-user-id';
const userCollectionKey = 'your-user-collection-key';

function ChatApp() {
  // 1. Initialize the chat store with useDittoChat
  useDittoChat({
    ditto: dittoInstance,
    userId: currentUserId,
    userCollectionKey: userCollectionKey,
  });

  // 2. Access parts of the store using useDittoChatStore selectors
  const rooms = useDittoChatStore(state => state.rooms);
  const activeRoomId = useDittoChatStore(state => state.activeRoomId);
  const setActiveRoomId = useDittoChatStore(state => state.setActiveRoomId);
  const messagesByRoom = useDittoChatStore(state => state.messagesByRoom);
  const createMessage = useDittoChatStore(state => state.createMessage);

  // 3. Derived state
  const activeRoom = rooms.find(r => r._id === activeRoomId);
  const messages = activeRoomId ? (messagesByRoom[activeRoomId] || []) : [];

  // Example usage:
  const handleSendMessage = (text: string) => {
    if (activeRoom) {
      createMessage(activeRoom, text);
    }
  };

  return (
    <div>
      <h1>Ditto Chat</h1>
      <p>Current User ID: {currentUserId}</p>
      <h2>Rooms:</h2>
      <ul>
        {rooms.map(room => (
          <li
            key={room._id}
            onClick={() => setActiveRoomId(room._id)}
            style={{ fontWeight: room._id === activeRoomId ? 'bold' : 'normal' }}
          >
            {room.name}
          </li>
        ))}
      </ul>
      <h2>Messages in Active Room:</h2>
      <ul>
        {messages.map(wrapper => (
          <li key={wrapper.id}>
            <strong>{wrapper.user?.name || wrapper.message.userId}:</strong> {wrapper.message.text}
          </li>
        ))}
      </ul>
      <button onClick={() => handleSendMessage('Hello from the app!')}>Send Test Message</button>
    </div>
  );
}

export default ChatApp;

Note: The dittoInstance, currentUserId, and userCollectionKey should be provided from your application's context or configuration. You will need to replace null with a properly initialized Ditto instance.

Comment Rooms (Generated Rooms)

DittoChatCore supports "Generated Rooms" which behave differently from standard chat rooms:

  1. They are excluded from the main room list found in state.rooms.
  2. They are stored separately in state.generatedRooms.
  3. They must be subscribed to explicitly.

Creating a Comment Room

Use createGeneratedRoom to create a room that won't pollute the main chat list:

const { createGeneratedRoom } = useDittoChatStore((state) => state)

const handleOpenComments = async (entityId: string) => {
  // Creates a room with ID `comments-${entityId}` if it doesn't exist
  // and adds it to state.generatedRooms
  await createGeneratedRoom(`comments-${entityId}`, 'Comments Thread')
}

Subscribing to Messages

For generated rooms, you often want to subscribe to messages only when viewing that specific thread. Use the subscribeToRoomMessages helper:

const { subscribeToRoomMessages, unsubscribeFromRoomMessages } =
  useDittoChatStore((state) => state)

useEffect(() => {
  if (isCommentsOpen) {
    // Subscribe to messages for this specific room
    subscribeToRoomMessages(commentRoomId, 'messages')
  }

  return () => {
    // Clean up subscription when closing/unmounting
    unsubscribeFromRoomMessages(commentRoomId)
  }
}, [isCommentsOpen, commentRoomId])

Role-Based Access Control (RBAC)

DittoChatCore includes a built-in RBAC system that allows you to control user permissions for various chat actions. By default, all permissions are enabled.

Available Permissions

| Permission | Description | Default | | ---------------------- | ------------------------- | ------- | | canCreateRoom | Create new chat rooms | true | | canEditOwnMessage | Edit own messages | true | | canDeleteOwnMessage | Delete own messages | true | | canAddReaction | Add reactions to messages | true | | canRemoveOwnReaction | Remove own reactions | true | | canMentionUsers | Mention users in messages | true | | canSubscribeToRoom | Subscribe to chat rooms | true |

Configuring Permissions

You can configure permissions when initializing the chat or update them dynamically:

import { useDittoChat, useDittoChatStore } from '@dittolive/ditto-chat-core'

// Configure permissions during initialization
const chat = useDittoChat({
  ditto: dittoInstance,
  userId: currentUserId,
  userCollectionKey: userCollectionKey,
  rbacConfig: {
    canCreateRoom: false, // Disable room creation
    canMentionUsers: false, // Disable user mentions
    canDeleteOwnMessage: true, // Allow deleting own messages
  },
})

// Or update permissions dynamically
const updateRBACConfig = useDittoChatStore((state) => state.updateRBACConfig)

updateRBACConfig({
  canEditOwnMessage: false, // Disable message editing
})

Checking Permissions

You can check if a user has permission to perform an action:

const canPerformAction = useDittoChatStore((state) => state.canPerformAction)

if (canPerformAction('canCreateRoom')) {
  // Show create room button
}

Note: When a permission is denied, the action will fail silently with a warning logged to the console. The UI layer should check permissions before displaying action buttons to provide better UX.

Notifications

DittoChatCore provides a customizable notification system through the notificationHandler prop. This allows you to integrate chat notifications with your preferred toast/notification library.

Notification Handler

The notificationHandler is an optional callback function that receives notification events from the chat system.

Signature:

notificationHandler?: (title: string, description: string) => void

Parameters:

  • title - The notification title (e.g., "New Message", "Room Created")
  • description - Additional details about the notification

Default Behavior

If no notificationHandler is provided, notifications will not trigger or be logged. You must provide a custom handler to receive and display notifications.

Custom Notification Handler

You can provide a custom handler to integrate with any toast/notification library:

Example with Sonner

import { toast } from 'sonner'
import { useDittoChat } from '@dittolive/ditto-chat-core'

useDittoChat({
  ditto: dittoInstance,
  userId: currentUserId,
  userCollectionKey: userCollectionKey,
  notificationHandler: (title, description) => {
    toast.info(title, {
      description,
    })
  },
})

Example with React-Toastify

import { toast } from 'react-toastify'
import { useDittoChat } from '@dittolive/ditto-chat-core'

useDittoChat({
  ditto: dittoInstance,
  userId: currentUserId,
  userCollectionKey: userCollectionKey,
  notificationHandler: (title, description) => {
    toast.info(`${title}: ${description}`)
  },
})

Example with Custom Notification System

import { useDittoChat } from '@dittolive/ditto-chat-core'

useDittoChat({
  ditto: dittoInstance,
  userId: currentUserId,
  userCollectionKey: userCollectionKey,
  notificationHandler: (title, description) => {
    // Custom notification logic
    showCustomNotification({
      type: 'info',
      title,
      message: description,
      duration: 3000,
    })
  },
})

Common Notification Events

The chat system triggers notifications for various events:

  • New messages in subscribed rooms
  • User mentions

Singleton Store Pattern

Important: DittoChatCore uses a global singleton pattern to ensure that all npm packages share the same Zustand store instance.

Why This Matters

When @dittolive/ditto-chat-core is installed in multiple packages (e.g., in your main app and in @dittolive/ditto-chat-ui), each package could potentially get its own copy of the module. To prevent multiple independent store instances, we use globalThis to maintain a single shared store.

What This Means for You

  1. Initialize once - Call useDittoChat() in your root component
  2. Use anywhere - Call useDittoChatStore() in any component from any package
  3. Shared state - All packages automatically share the same state

Example

// In your main app (initialize once)
function App() {
  useDittoChat({
    ditto: dittoInstance,
    userId: currentUserId,
    userCollectionKey: userCollectionKey,
  })

  return <YourApp />
}

// In any component, from any package (use the shared store)
function ChatComponent() {
  const messages = useDittoChatStore((state) => state.messagesByRoom)
  // All components see the same state
}

Architecture & Performance

Optimistic UI Updates

DittoChatCore uses an optimistic update pattern for message reactions to provide instant UI feedback.

How it works:

  1. Immediate UI Update - Reaction appears instantly in the UI before any database operations
  2. Async Persistence - Change persists to Ditto database in the background
  3. Auto Rollback - If database update fails, the reaction is automatically removed from the UI

Benefits: Instant feedback, no perceived latency, automatic data consistency.

Implementation: See updateMessageReactions() function in src/slices/useMessages.ts for the complete implementation details.

Available Scripts

In the project directory, you can run:

  • npm run build: Builds the project for production.
  • npm run test: Runs tests and exits.
  • npm run test:watch: Runs tests in watch mode.
  • npm run clean: Removes the dist directory.

Keywords

react, typescript, ditto, ditto-chat-core, ditto-chat

License

MIT

Repository

https://github.com/getditto/DittoChat.git