Common Patterns

Code examples library for typical tasks

Displaying Code Examples

Quick Decision Guide

Need to show code?
├─ Inline snippet? → <Code>
└─ Multi-line block?
   ├─ No syntax colors needed? → <Code inline={false}>
   └─ With syntax highlighting? → <CollapsibleCodeBlock>

Correct - Multi-Color Syntax Highlighting

import { CollapsibleCodeBlock } from '@thesage/ui';

// [Recommended]: Automatic syntax highlighting
<CollapsibleCodeBlock
  id="unique-id"
  code={`const greeting = "Hello World";
console.log(greeting);`}
  defaultCollapsed={false}
  showCopy={true}
/>

Avoid - Single Color, No Highlighting

import { Code } from '@thesage/ui';

// [Avoid]: Single-color text, no syntax highlighting
<Code inline={false}>{`const greeting = "Hello World";
console.log(greeting);`}</Code>

When to Use Each

<Code>- Inline code snippets like useState or single-line commands like pnpm install
<Code inline={false}>- Plain text blocks where syntax highlighting isn't needed (configs, plain text output)
<CollapsibleCodeBlock>- Multi-line code examples that benefit from syntax highlighting, copy button, and collapsible UI

Pro tip: CollapsibleCodeBlock automatically tokenizes string code for syntax highlighting. Just pass your code as a string - no manual tokenization needed! Each block needs a unique id prop.

Using Design Tokens

Accessing design tokens via CSS variables

export function MyComponent() {
  return (
    <div className="bg-[var(--color-primary)] text-[var(--color-primary-foreground)]">
      <h1 className="text-[var(--font-size-heading-1)]">
        Hello World
      </h1>
      <p className="text-[var(--color-text-secondary)]">
        This component uses design tokens for theming
      </p>
    </div>
  );
}

Pro tip: Always use CSS variables (var(--token-name)) instead of hardcoded values. This ensures your component automatically adapts to theme changes.

Creating Theme-Aware Components

Components that adapt to the current theme

import { useTheme } from '@thesage/ui';

export function ThemedCard() {
  const { theme } = useTheme();

  return (
    <div
      className="p-6 rounded-lg"
      style={{
        backgroundColor: 'var(--color-surface)',
        border: '1px solid var(--color-border)',
      }}
    >
      <p className="text-[var(--color-text-primary)]">
        Current theme: {theme}
      </p>
    </div>
  );
}

Adding Motion with Preference Detection

Using useMotionPreference hook with Framer Motion

import { motion } from 'framer-motion';
import { useMotionPreference } from '@thesage/ui';

export function AnimatedCard() {
  const { shouldAnimate, scale } = useMotionPreference();

  // Scale 5 (default) = 1x duration
  // Duration: 0.3 is base duration for this animation
  const duration = shouldAnimate && scale > 0 
    ? 0.3 * (5 / scale) 
    : 0;

  return (
    <motion.div
      initial={shouldAnimate ? { opacity: 0, y: 20 } : false}
      animate={{ opacity: 1, y: 0 }}
      transition={{ duration, ease: 'easeOut' }}
      className="p-6 bg-[var(--color-surface)] rounded-lg"
    >
      <p>This card respects motion preferences</p>
    </motion.div>
  );
}

Accessibility first: When shouldAnimate is false, set duration to 0 and disable position/scale animations. This respects user preferences and system settings.

Responsive Design Pattern

Mobile-first responsive component

export function ResponsiveGrid() {
  return (
    <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
      <Card className="p-4">Item 1</Card>
      <Card className="p-4">Item 2</Card>
      <Card className="p-4">Item 3</Card>
    </div>
  );
}

Breakpoints:

  • sm: 640px (phones in landscape)
  • md: 768px (tablets)
  • lg: 1024px (desktops)
  • xl: 1280px (large desktops)

Composing Components

Building a SearchBar from Input and Button components

import { Input, Button } from '@thesage/ui';

interface SearchBarProps {
  placeholder?: string;
  onSearch: (query: string) => void;
}

export function SearchBar({ placeholder, onSearch }: SearchBarProps) {
  const [query, setQuery] = useState('');

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    onSearch(query);
  };

  return (
    <form onSubmit={handleSubmit} className="flex gap-2">
      <Input
        value={query}
        onChange={(e) => setQuery(e.target.value)}
        placeholder={placeholder || 'Search...'}
        className="flex-1"
      />
      <Button type="submit" variant="default">
        Search
      </Button>
    </form>
  );
}

Form Handling Pattern

Using the useForm hook for form validation

import { useForm, TextField, Button } from '@thesage/ui';

export function LoginForm() {
  const { values, errors, handleChange, handleSubmit } = useForm({
    initialValues: { email: '', password: '' },
    validate: (values) => {
      const errors: Record<string, string> = {};
      if (!values.email) errors.email = 'Email is required';
      if (!values.password) errors.password = 'Password is required';
      return errors;
    },
    onSubmit: (values) => {
      console.log('Form submitted:', values);
    },
  });

  return (
    <form onSubmit={handleSubmit} className="space-y-4">
      <TextField
        label="Email"
        name="email"
        type="email"
        value={values.email}
        onChange={handleChange}
        error={errors.email}
      />
      <TextField
        label="Password"
        name="password"
        type="password"
        value={values.password}
        onChange={handleChange}
        error={errors.password}
      />
      <Button type="submit" variant="default">
        Log In
      </Button>
    </form>
  );
}

Toast Notifications Pattern

Using the useToast hook for notifications

import { useToast, Button, ToastProvider } from '@thesage/ui';

function MyComponent() {
  const { toast } = useToast();

  const showSuccess = () => {
    toast('Success!', 'success');
  };

  const showError = () => {
    toast('Something went wrong', 'error');
  };

  return (
    <div>
      <Button onClick={showSuccess}>Show Success</Button>
      <Button onClick={showError}>Show Error</Button>
    </div>
  );
}

// Wrap your app with ToastProvider
export function App() {
  return (
    <ToastProvider>
      <MyComponent />
    </ToastProvider>
  );
}

Modal Pattern

Using the Modal component with state management

import { useState } from 'react';
import { Modal, Button } from '@thesage/ui';

export function ConfirmDialog() {
  const [isOpen, setIsOpen] = useState(false);

  const handleConfirm = () => {
    console.log('Confirmed!');
    setIsOpen(false);
  };

  return (
    <>
      <Button onClick={() => setIsOpen(true)}>
        Open Dialog
      </Button>

      <Modal
        isOpen={isOpen}
        onClose={() => setIsOpen(false)}
        title="Confirm Action"
        footer={
          <>
            <Button variant="secondary" onClick={() => setIsOpen(false)}>
              Cancel
            </Button>
            <Button variant="default" onClick={handleConfirm}>
              Confirm
            </Button>
          </>
        }
      >
        <p>Are you sure you want to proceed?</p>
      </Modal>
    </>
  );
}