Skip to main content

Quick Start

This guide will help you build your first application using Zayne Labs Toolkit. We’ll create a simple interactive component that demonstrates core utilities, React hooks, and type safety.

What We’ll Build

A toggle component with clipboard functionality that showcases:
  • Core utilities (copyToClipboard, debounce)
  • React hooks (useToggle, useDisclosure, useClickOutside)
  • Type safety with TypeScript
1

Install the packages

First, install the packages you need:
npm install @zayne-labs/toolkit-core @zayne-labs/toolkit-react @zayne-labs/toolkit-type-helpers
Make sure you have React 19.0.0 or higher installed in your project.
2

Create your first component

Create a new file InteractiveCard.tsx and add the following code:
InteractiveCard.tsx
import { copyToClipboard } from "@zayne-labs/toolkit-core";
import { useToggle, useDisclosure } from "@zayne-labs/toolkit-react";
import { useState } from "react";
import type { Prettify } from "@zayne-labs/toolkit-type-helpers";

type CardState = Prettify<{
  isExpanded: boolean;
  copyCount: number;
}>;

export function InteractiveCard() {
  const [isExpanded, toggleExpanded] = useToggle(false);
  const [copyCount, setCopyCount] = useState(0);
  
  const modal = useDisclosure({
    initialState: false,
    hasScrollControl: true,
  });

  const handleCopy = async () => {
    await copyToClipboard("Hello from Zayne Labs Toolkit!", {
      onSuccess: () => {
        setCopyCount((prev) => prev + 1);
        console.log("Copied to clipboard!");
      },
      onError: (error) => {
        console.error("Failed to copy:", error);
      },
    });
  };

  return (
    <div className="card">
      <h2>Interactive Card</h2>
      
      <button onClick={() => toggleExpanded()}>
        {isExpanded ? "Collapse" : "Expand"}
      </button>
      
      {isExpanded && (
        <div className="content">
          <p>This content is toggleable!</p>
          
          <button onClick={handleCopy}>
            Copy to Clipboard
          </button>
          
          {copyCount > 0 && (
            <p>Copied {copyCount} time{copyCount > 1 ? "s" : ""}!</p>
          )}
        </div>
      )}
      
      <button onClick={() => modal.onOpen()}>
        Open Modal
      </button>
      
      {modal.isOpen && (
        <div className="modal">
          <h3>Modal is Open</h3>
          <p>Scroll is locked when modal is open</p>
          <button onClick={() => modal.onClose()}>
            Close Modal
          </button>
        </div>
      )}
    </div>
  );
}
This component demonstrates:
  • useToggle for expandable content
  • copyToClipboard with success/error callbacks
  • useDisclosure with scroll lock for modals
  • Prettify type helper for clean type definitions
3

Use advanced hooks

Let’s enhance the component with more hooks. Create AdvancedCard.tsx:
AdvancedCard.tsx
import { debounce } from "@zayne-labs/toolkit-core";
import { 
  useClickOutside, 
  useCallbackRef,
  useDebounce 
} from "@zayne-labs/toolkit-react";
import { composeRefs } from "@zayne-labs/toolkit-react/utils";
import { useRef, useState } from "react";

export function AdvancedCard() {
  const [search, setSearch] = useState("");
  const [isMenuOpen, setIsMenuOpen] = useState(false);
  const menuRef = useRef<HTMLDivElement>(null);
  const inputRef = useRef<HTMLInputElement>(null);
  
  // Debounce search input
  const debouncedSearch = useDebounce(search, 300);
  
  // Close menu when clicking outside
  const handleClickOutside = useCallbackRef(() => {
    setIsMenuOpen(false);
  });
  
  useClickOutside(menuRef, handleClickOutside, {
    enabled: isMenuOpen,
  });

  return (
    <div className="advanced-card">
      <input
        ref={inputRef}
        type="text"
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        placeholder="Search..."
      />
      
      <p>Debounced value: {debouncedSearch}</p>
      
      <button onClick={() => setIsMenuOpen(!isMenuOpen)}>
        Toggle Menu
      </button>
      
      {isMenuOpen && (
        <div ref={menuRef} className="menu">
          <p>Click outside to close</p>
          <ul>
            <li>Menu Item 1</li>
            <li>Menu Item 2</li>
            <li>Menu Item 3</li>
          </ul>
        </div>
      )}
    </div>
  );
}
This demonstrates:
  • useDebounce for search input optimization
  • useClickOutside for dropdown menus
  • useCallbackRef for stable callbacks
  • Ref composition with composeRefs
4

Use React utilities

The toolkit provides powerful React utilities. Here’s an example using slot components:
SlotExample.tsx
import {
  createSlotComponent,
  getSlotMap,
  composeEventHandlers,
  type GetSlotComponentProps,
} from "@zayne-labs/toolkit-react/utils";

type DialogSlots =
  | GetSlotComponentProps<"header">
  | GetSlotComponentProps<"content">
  | GetSlotComponentProps<"footer">;

function Dialog(props: { children: React.ReactNode }) {
  const { children } = props;
  const slots = getSlotMap<DialogSlots>(children);

  const handleClick = composeEventHandlers(
    () => console.log("Dialog clicked"),
    () => console.log("Additional handler")
  )();

  return (
    <div className="dialog" onClick={handleClick}>
      <header>{slots.header}</header>
      <div className="content">{slots.content}</div>
      <footer>{slots.footer}</footer>
      <aside>{slots.default}</aside>
    </div>
  );
}

Dialog.Slot = createSlotComponent<DialogSlots>();

// Usage
export function DialogExample() {
  return (
    <Dialog>
      <Dialog.Slot name="header">
        <h2>Dialog Title</h2>
      </Dialog.Slot>
      
      <Dialog.Slot name="content">
        <p>This is the dialog content</p>
      </Dialog.Slot>
      
      <Dialog.Slot name="footer">
        <button>Close</button>
      </Dialog.Slot>
      
      <p>This goes in the default slot</p>
    </Dialog>
  );
}
5

Use type helpers

Leverage TypeScript utilities for better type safety:
types.ts
import type {
  Prettify,
  DeepPrettify,
  NonFalsy,
  PrettyOmit,
  CallbackFn,
} from "@zayne-labs/toolkit-type-helpers";
import { isString, isBoolean, isNumber } from "@zayne-labs/toolkit-type-helpers";

// Clean up intersection types
type User = Prettify<{ id: number } & { name: string } & { email: string }>;
// Result: { id: number; name: string; email: string }

// Deep prettify nested objects
type NestedData = DeepPrettify<{
  user: { id: number } & { profile: { name: string } };
}>;

// Remove falsy types
type NonFalsyString = NonFalsy<string | null | undefined>; // string

// Omit with prettified result
type UserWithoutEmail = PrettyOmit<User, "email">;

// Type-safe callbacks
const handleUserUpdate: CallbackFn<User> = (user) => {
  console.log(user.name);
};

// Runtime type guards
function processValue(value: unknown) {
  if (isString(value)) {
    // TypeScript knows value is string here
    return value.toUpperCase();
  }
  
  if (isNumber(value)) {
    // TypeScript knows value is number here
    return value.toFixed(2);
  }
  
  if (isBoolean(value)) {
    // TypeScript knows value is boolean here
    return value ? "yes" : "no";
  }
  
  return "unknown";
}
6

Run your application

Add the components to your main app:
App.tsx
import { InteractiveCard } from "./InteractiveCard";
import { AdvancedCard } from "./AdvancedCard";
import { DialogExample } from "./SlotExample";

export default function App() {
  return (
    <div className="app">
      <h1>Zayne Labs Toolkit Demo</h1>
      
      <InteractiveCard />
      <AdvancedCard />
      <DialogExample />
    </div>
  );
}
Start your development server and test the components!

Common Patterns

Debouncing and Throttling

import { debounce, throttle } from "@zayne-labs/toolkit-core";
import { useDebounce, useThrottle } from "@zayne-labs/toolkit-react";

// Core utilities
const debouncedFn = debounce((value: string) => {
  console.log("Debounced:", value);
}, 300);

const throttledFn = throttle((value: string) => {
  console.log("Throttled:", value);
}, 1000);

// React hooks
function SearchComponent() {
  const [search, setSearch] = useState("");
  const debouncedSearch = useDebounce(search, 300);
  const throttledSearch = useThrottle(search, 1000);
  
  // Use debouncedSearch for API calls
  // Use throttledSearch for analytics
}

State Management

import { createStore } from "@zayne-labs/toolkit-core";
import { useStore } from "@zayne-labs/toolkit-react";

// Create a store
const counterStore = createStore({ count: 0 });

// Use in React
function Counter() {
  const count = useStore(counterStore, (state) => state.count);
  
  return (
    <button onClick={() => counterStore.setState({ count: count + 1 })}>
      Count: {count}
    </button>
  );
}

Next Steps

Core Utilities

Explore all available core utilities.

React Hooks

Discover all React hooks.

Type Helpers

Learn about TypeScript utilities.

API Reference

View the complete API reference.
All utilities are tree-shakeable. Your bundler will only include what you actually use, keeping your bundle size minimal.

Best Practices

  1. Import Specific Utilities: Import only what you need for optimal tree-shaking
    // Good
    import { useToggle, useDisclosure } from "@zayne-labs/toolkit-react";
    
    // Avoid
    import * as toolkit from "@zayne-labs/toolkit-react";
    
  2. Use Type Helpers: Leverage TypeScript utilities for cleaner types
    type Props = Prettify<BaseProps & AdditionalProps>;
    
  3. Combine Hooks: Hooks are composable and work well together
    const modal = useDisclosure();
    const [data, setData] = useStorageState("key", defaultValue);
    
  4. Leverage Callbacks: Use useCallbackRef for stable function references
    const handleClick = useCallbackRef(() => {
      // Always gets latest closure values
    });
    

Build docs developers (and LLMs) love