Skip to content

Components Reference

Complete guide to the Constellation component library and patterns.

Component Organization

src/components/
โ”œโ”€โ”€ ui/                    # Shadcn/ui base components
โ”œโ”€โ”€ common/                # Shared across features
โ”œโ”€โ”€ admin/                 # Admin-specific components
โ”œโ”€โ”€ dashboard/             # Dashboard feature
โ”œโ”€โ”€ editor/                # Editor feature
โ”œโ”€โ”€ forms/                 # Form components
โ”œโ”€โ”€ graph/                 # Graph visualization
โ”œโ”€โ”€ mapEditor/             # Map editor
โ”œโ”€โ”€ navigation/            # Navigation
โ”œโ”€โ”€ plate-ui/              # Editor plugins
โ””โ”€โ”€ context/               # Context providers

UI Component Library (Shadcn/ui)

Base components imported from Shadcn/ui at src/components/ui/.

Available Components

Component Location Use Case
Button ui/Button Clickable buttons
Card ui/Card Content containers
Dialog ui/Dialog Modal dialogs
Form ui/Form Form wrapper
Input ui/Input Text input fields
Select ui/Select Dropdown selection
Tabs ui/Tabs Tabbed content
Alert ui/Alert Alert messages
Badge ui/Badge Status labels
Dropdown ui/Dropdown Dropdown menus

Button Component

import { Button } from '@/src/components/ui/Button';

// Basic button
<Button>Click me</Button>

// Variants
<Button variant="destructive">Delete</Button>
<Button variant="outline">Secondary</Button>
<Button variant="ghost">Tertiary</Button>

// Sizes
<Button size="sm">Small</Button>
<Button size="lg">Large</Button>

// States
<Button disabled>Disabled</Button>
<Button isLoading>Loading...</Button>

// Full width
<Button className="w-full">Full width</Button>

Card Component

import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/src/components/ui/Card';

<Card>
  <CardHeader>
    <CardTitle>Title</CardTitle>
    <CardDescription>Description</CardDescription>
  </CardHeader>
  <CardContent>
    Content here
  </CardContent>
</Card>

Form Component

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { Form, FormControl, FormField, FormItem, FormLabel } from '@/src/components/ui/Form';
import { Input } from '@/src/components/ui/Input';

const formSchema = z.object({
  email: z.string().email(),
  name: z.string().min(2),
});

export function MyForm() {
  const form = useForm({
    resolver: zodResolver(formSchema),
  });

  return (
    <Form {...form}>
      <form onSubmit={form.handleSubmit(onSubmit)}>
        <FormField
          control={form.control}
          name="email"
          render={({ field }) => (
            <FormItem>
              <FormLabel>Email</FormLabel>
              <FormControl>
                <Input placeholder="Email" {...field} />
              </FormControl>
            </FormItem>
          )}
        />
      </form>
    </Form>
  );
}

Common Components

Located: src/components/common/Header.tsx

import { Header } from '@/src/components/common/Header';

<Header />

Props: None (uses context for auth state)

Features: - User menu - Logo - Navigation links - Dark mode toggle

Located: src/components/common/Navigation.tsx

import { Navigation } from '@/src/components/common/Navigation';

<Navigation />

Located: src/components/common/Footer.tsx

import { Footer } from '@/src/components/common/Footer';

<Footer />

Dashboard Components

DashboardLayout

import { DashboardLayout } from '@/src/components/dashboard/DashboardLayout';

<DashboardLayout>
  {/* Dashboard content */}
</DashboardLayout>

Props: - children - Dashboard content

WorkspaceCard

interface WorkspaceCardProps {
  workspace: Workspace;
  onSelect?: (workspace: Workspace) => void;
  onDelete?: (workspaceId: string) => void;
}

<WorkspaceCard
  workspace={workspace}
  onSelect={handleSelect}
  onDelete={handleDelete}
/>

Features: - Workspace thumbnail - Name and description - Last modified date - Actions menu

StatCard

<StatCard
  title="Total Projects"
  value={42}
  icon={<ProjectIcon />}
  trend="+12%"
/>

Editor Components (Plate.js)

PlateEditor

Main editor component:

import { PlateEditor } from '@/src/components/editor/PlateEditor';

<PlateEditor
  content={content}
  onChange={handleChange}
  readOnly={false}
/>

Props: - content - Initial editor content - onChange - Callback on content change - readOnly - Disable editing - plugins - Custom plugins array

EditorToolbar

import { EditorToolbar } from '@/src/components/editor/EditorToolbar';

<EditorToolbar />

Features: - Text formatting (bold, italic, underline) - Lists and indentation - Link insertion - Code blocks - Mentions and emojis

Plate Plugins

Available plugins in src/components/plate-ui/:

Plugin Description
BoldPlugin Bold text formatting
ItalicPlugin Italic text formatting
UnderlinePlugin Underline text
CodePlugin Inline code
BlockquotePlugin Block quotes
ListPlugin Ordered/unordered lists
LinkPlugin Hyperlinks
ImagePlugin Image insertion
MentionPlugin @mentions
EmojiPlugin Emoji picker
SlashCommandPlugin Slash commands
TablePlugin Table insertion
CodeBlockPlugin Code blocks

Admin Components

UserManagement

import { UserManagement } from '@/src/components/admin/UserManagement';

<UserManagement />

Features: - User list with pagination - Create/edit/delete users - Role management - Search and filter

ContentModeration

import { ContentModeration } from '@/src/components/admin/ContentModeration';

<ContentModeration />

Features: - Review reported content - Approve/reject content - Flag management

SystemSettings

import { SystemSettings } from '@/src/components/admin/SystemSettings';

<SystemSettings />

Features: - Global configuration - Feature flags - Notification settings


Graph Components

GraphViewer

import { GraphViewer } from '@/src/components/graph/GraphViewer';

interface Node {
  id: string;
  label: string;
}

interface Edge {
  source: string;
  target: string;
}

<GraphViewer
  nodes={nodes}
  edges={edges}
  onNodeClick={handleNodeClick}
  interactive={true}
/>

GraphEditor

import { GraphEditor } from '@/src/components/graph/GraphEditor';

<GraphEditor
  graph={graph}
  onChange={handleChange}
/>

Form Components

UserForm

import { UserForm } from '@/src/components/forms/UserForm';

<UserForm
  user={user}
  onSubmit={handleSubmit}
  isLoading={false}
/>

WorkspaceForm

import { WorkspaceForm } from '@/src/components/forms/WorkspaceForm';

<WorkspaceForm
  workspace={workspace}
  onSubmit={handleSubmit}
  isLoading={false}
/>

Context Providers

AuthProvider

import { AuthProvider } from '@/src/components/context/AuthProvider';

<AuthProvider>
  <App />
</AuthProvider>

Provides: Authentication state, user info, login/logout functions

WorkspaceProvider

import { WorkspaceProvider } from '@/src/components/context/WorkspaceProvider';

<WorkspaceProvider>
  <WorkspaceContent />
</WorkspaceProvider>

Provides: Current workspace, workspace list, workspace operations

LanguageProvider

import { LanguageProvider } from '@/src/components/context/LanguageProvider';

<LanguageProvider>
  <App />
</LanguageProvider>

Provides: Current language, translation function, language switcher

ThemeProvider

import { ThemeProvider } from '@/src/components/context/ThemeProvider';

<ThemeProvider>
  <App />
</ThemeProvider>

Provides: Current theme, theme switcher


Creating New Components

Component Template

// src/components/[feature]/MyNewComponent.tsx
'use client';

import React from 'react';
import { Button } from '@/src/components/ui/Button';
import type { MyComponentProps } from './MyNewComponent.types';

/**
 * Component description
 * @param props - Component props
 * @returns JSX element
 */
export function MyNewComponent({ title, onAction }: MyComponentProps) {
  const [state, setState] = React.useState(false);

  const handleAction = () => {
    onAction?.();
    setState(true);
  };

  return (
    <div className="p-4">
      <h2 className="font-bold">{title}</h2>
      <Button onClick={handleAction}>Click me</Button>
    </div>
  );
}

Types File

// src/components/[feature]/MyNewComponent.types.ts
export interface MyComponentProps {
  title: string;
  onAction?: () => void;
}

Test File

// src/components/[feature]/MyNewComponent.test.tsx
import { describe, it, expect, vi } from 'vitest';
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { MyNewComponent } from './MyNewComponent';

describe('MyNewComponent', () => {
  it('should render with title', () => {
    render(<MyNewComponent title="Test" />);
    expect(screen.getByText('Test')).toBeInTheDocument();
  });

  it('should call onAction when button clicked', async () => {
    const user = userEvent.setup();
    const handleAction = vi.fn();

    render(<MyNewComponent title="Test" onAction={handleAction} />);

    await user.click(screen.getByRole('button'));
    expect(handleAction).toHaveBeenCalled();
  });
});

Component Best Practices

โœ… Do's

  • โœ… Keep components small and focused
  • โœ… Extract props to TypeScript interfaces
  • โœ… Use composition for complex components
  • โœ… Memoize when performance matters
  • โœ… Write tests for components
  • โœ… Document with JSDoc comments
  • โœ… Use semantic HTML
  • โœ… Separate concerns (UI, logic, services)

โŒ Don'ts

  • โŒ Don't make components too large (> 300 lines)
  • โŒ Don't mix presentation and business logic
  • โŒ Don't use inline styles
  • โŒ Don't pass too many props (> 5)
  • โŒ Don't use any types
  • โŒ Don't hardcode strings (use i18n)
  • โŒ Don't make API calls in render
  • โŒ Don't forget loading/error states


Last Updated: January 2026