Weave.js

Set up the frontend

Learn how to set up the frontend of a Weave.js app on Next.js

In this guide, you will learn how to set up the frontend of a collaborative application using Weave.js and Next.js.

To build the collaborative canvas interface, you will create two pages:

  • Enter room page: Let users indicate the room name or ID and a username to join a collaboration room.
  • Room page: Handles the UI for the collaboration room after a user joins.

Opinionated tools

This guide uses some opinionated tools and libraries (like shadcn/ui for UI components) to demonstrate the integration. Adapt or replace these tools to match your project's needs and preferences.

Prerequisites

Before you begin, ensure that you have completed the backend installation guide.

Step by step

To set up Weave.js frontend over our Next.js project (on a single artifact), follow these steps:

Install the Weave.js frontend dependencies

From the project root folder, install the Weave.js frontend dependencies:

npm install @inditextech/weave-react

This example uses the Weave.js React wrapper.

Install the additional frontend dependencies

From the project root, install supporting frontend libraries:

npm install boring-avatars class-variance-authority clsx @hookform/resolvers lucide-react react-hook-form tailwindcss-animate tailwind-merge @tanstack/react-query zod

These packages provide auxiliary functionality such as:

  • Form handling: react-hook-form, @hookform/resolvers, zod

  • UI utilities: clsx, class-variance-authority, tailwind-merge, tailwindcss-animate

  • Icons and avatars: lucide-react, boring-avatars

  • State management: @tanstack/react-query

Set up a global state store for the UI

Create a global state store to handle the UI state. This store manages the current user, room ID, and UI visibility state across the application. Use Zustand for this.

  • Install the Zustand dependency:

    npm install zustand
  • Create a store/store.ts file in the project root folder and set its content to:

    store/store.ts
    import { create } from "zustand";
    
    type ShowcaseUser = {
      id: string;
      name: string;
      email: string;
    };
    
    interface CollaborationRoomState {
      ui: {
        show: boolean;
      };
      user: ShowcaseUser | undefined;
      room: string | undefined;
      setShowUi: (newShowUI: boolean) => void;
      setUser: (newUser: ShowcaseUser | undefined) => void;
      setRoom: (newRoom: string | undefined) => void;
    }
    
    export const useCollaborationRoom = create<CollaborationRoomState>()((set) => ({
      ui: {
        show: true,
      },
      user: undefined,
      room: undefined,
      setShowUi: (newShowUI) =>
        set((state) => ({
          ...state,
          ui: { ...state.ui, show: newShowUI },
        })),
      setUser: (newUser) => set((state) => ({ ...state, user: newUser })),
      setRoom: (newRoom) => set((state) => ({ ...state, room: newRoom })),
    }));

Configure utility functions

Create a lib/utils.ts file in the project root folder to provide utility functions used throughout the UI components.

Create the file with the following content:

lib/utils.ts
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

export function getContrastTextColor(hex: string): "white" | "black" {
  // Remove "#" if present
  const cleaned = hex.replace(/^#/, "");

  // Parse R, G, B from hex
  const r = parseInt(cleaned.slice(0, 2), 16);
  const g = parseInt(cleaned.slice(2, 4), 16);
  const b = parseInt(cleaned.slice(4, 6), 16);

  // Calculate luminance (per W3C)
  const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;

  // Return black for light colors, white for dark
  return luminance > 0.5 ? "black" : "white";
}

export function stringToColor(str: string) {
  let hash = 0;
  str.split("").forEach((char) => {
    hash = char.charCodeAt(0) + ((hash << 5) - hash);
  });
  let color = "#";
  for (let i = 0; i < 3; i++) {
    const value = (hash >> (i * 8)) & 0xff;
    color += value.toString(16).padStart(2, "0");
  }
  return color;
}

This file includes:

  • cn function: Merges Tailwind CSS class names with proper conflict resolution using clsx and tailwind-merge. This ensures that when you combine multiple class strings, conflicting Tailwind utilities are resolved correctly (e.g., p-4 and p-2 will result in p-2).

  • getContrastTextColor function: Determines whether white or black text provides better contrast against a given hex color background. It calculates the relative luminance of the color using the W3C standard formula and returns the appropriate text color for readability.

  • stringToColor function: Generates a consistent hex color from any string input using a hash function. This is useful for creating deterministic color assignments (like avatar backgrounds) based on user names or other identifiers.

Set up the Enter Room page

With the global state store in place, create the Enter Room page.

Set up the Room page

Now that the Enter Room page is set up, create the Room page.

Next steps

Finally, run the project.