<iframe src="https://www.googletagmanager.com/ns.html?id=GTM-PP7S83N" height="0" width="0" style="display:none;visibility:hidden">
Tutorial

Add a feature complete messenger to your Refine app

This tutorial will show how to  add a full-featured messenger to your Refine powered React app using our messenger component.

Have your favorite code editor ready, and let's get started!

1. Prepare your Refine app

Make sure you have a Refine powered app. For this tutorial we make use of NextJS with Ant Design.

Refine has a ready-to-go Authentication with NextAuth.js example for this.

npm create refine-app@latest -- --example with-nextjs-next-auth

2. Configuring Weavy

We need Weavy to be configured with an environment url and authentication to get the components running.

Make sure you have followed the tutorial for What to do first with Refine to configure authentication.

3. Add navigation a helper

We will add a stateful navigation layer on top of the refine navigation. This will take advantage of #hashes in the URL. To be able to easily take advantage of hash changes, we need to add a useHash() hook in /src/hooks/hash/useHash.ts
/src/hooks/hash/useHash.ts
"use client";
import { useParams } from "next/navigation";
import { useEffect, useState } from "react";

/**
 * Gets the hash from the current location.
 * @returns The current hash without a leading #hash sign
 */
const getHash = () =>
  typeof window !== "undefined" ? window.location.hash.replace("#", "") : "";

/**
 * Returns the hash from the current location. 
 * Updates when the hash is changed by the browser or by next navigation.
 * 
 * @returns Any current hash without leading #hash sign
 */
const useHash = () => {
  const [isClient, setIsClient] = useState(false);
  const [hash, setHash] = useState(getHash());
  const params = useParams();

  useEffect(() => {
    const handleHashChange = () => {
      setHash(getHash());
    };
    window.addEventListener('hashchange', handleHashChange);
    return () => {
      window.removeEventListener('hashchange', handleHashChange);
    };
  }, []);

  useEffect(() => {
    setIsClient(true);
    setHash(getHash());
  }, [params]);

  return isClient ? hash : "";
};

export default useHash;

4. Create a Messenger component

We're adding the messenger to a drawer triggered from the navigation header. We need to add a button and a drawer. Then we need to make it open on specific navigation requests.

  • Create a new component in /src/app/components/weavy/messenger.tsx.
  • Import a <Drawer> component from antd and make it use an open boolean state. Set the initial state to false, so the drawer will be hidden initially.
  • Import the useHash hook we created and connect it to the open state and make it listen to the #messenger hash. Whenever the #messenger hash is present in the URL, the drawer will open.
  • Import a <Button> component from antd and make it toggle the open state on clicks. Set the icon attribute to the <MessageOutlined /> icon from 
    @ant-design/icons.
/src/app/components/weavy/messenger.tsx
import React, { useEffect, useState } from "react"
import { Button, Drawer } from "antd"
import { WyMessenger } from "@weavy/uikit-react"
import { MessageOutlined } from "@ant-design/icons"
import { useGo } from "@refinedev/core"
import useHash from "@hooks/hash/useHash"

export const WeavyMessenger: React.FC = () => {
  const [open, setOpen] = useState(false)
  const go = useGo()
  const hash = useHash()

  const showDrawer = () => {
    setOpen(true)
  }

  const closeDrawer = () => {
    setOpen(false)
    if (hash === "messenger") {
      // Clear the hash when closing the drawer
      go({ hash: "" })
    }
  }

  useEffect(() => {
    // Show the drawer when the #messenger hash is set
    if (hash === "messenger") {
      showDrawer()
    }
  }, [hash])

  return (
    <>
      <Button
        type="default"
        onClick={showDrawer}
        title="Messenger"
        // @ts-expect-error Ant Design Icon's v5.0.1 has an issue with @types/react@^18.2.66
        icon={<MessageOutlined />}
      ></Button>
      <Drawer onClose={closeDrawer} open={open} styles={{ body: { padding: 0, display: "flex" } }}>
        <WyMessenger />
      </Drawer>
    </>
  )
}

5. Add the component in the layout

Now that the component is ready, we just need to add it in our navigation bar. To make everything align nicely, we'll change the <Space> layouts to <Flex> layouts.

  • Open /src/components/header/index.tsx.
  • Change the outer <Space> layout to a <Flex align="center" gap="small"> layout.
  • Change the avatar <Space> layout to a <Flex style={{ marginLeft: "8px" }} align="center" gap="middle"> layout.
  • Import and add the <WeavyMessenger /> component you created last in the outer <Space> layout.
/src/components/header/index.tsx
"use client"
import { WeavyMessenger } from "@components/weavy/messenger"
import { ColorModeContext } from "@contexts/color-mode"
import type { RefineThemedLayoutV2HeaderProps } from "@refinedev/antd"
import { useGetIdentity } from "@refinedev/core"
import { Layout as AntdLayout, Avatar, Flex, Switch, Typography, theme } from "antd"
import React, { useContext } from "react"

const { Text } = Typography
const { useToken } = theme

type IUser = {
  id: number
  name: string
  avatar: string
}

export const Header: React.FC<RefineThemedLayoutV2HeaderProps> = ({ sticky }) => {
  const { token } = useToken()
  const { data: user } = useGetIdentity<IUser>()
  const { mode, setMode } = useContext(ColorModeContext)

  const headerStyles: React.CSSProperties = {
    backgroundColor: token.colorBgElevated,
    display: "flex",
    justifyContent: "flex-end",
    alignItems: "center",
    padding: "0px 24px",
    height: "64px",
  }

  if (sticky) {
    headerStyles.position = "sticky"
    headerStyles.top = 0
    headerStyles.zIndex = 1
  }

  return (
    <AntdLayout.Header style={headerStyles}>
      <Flex align="center" gap="small">
        <Switch checkedChildren="🌛" unCheckedChildren="🔆" onChange={() => setMode(mode === "light" ? "dark" : "light")} defaultChecked={mode === "dark"} />
        {(user?.name || user?.avatar) && (
          <Flex style={{ marginLeft: "8px" }} align="center" gap="middle">
            {user?.name && <Text strong>{user.name}</Text>}
            {user?.avatar && <Avatar src={user?.avatar} alt={user?.name} />}
          </Flex>
        )}
        <WeavyMessenger />
      </Flex>
    </AntdLayout.Header>
  )
}

6. Done!

The messenger is now ready for use. We have also prepared the drawer for navigation requests. Take it for a spin!

refine-messenger
Ask AI
Support

To access live chat with our developer success team you need a Weavy account.

Sign in or create a Weavy account