Skip to main content

Quickstart Guide

Get up and running with Stride in under 5 minutes. This guide walks you through creating a complete user profile form with brand theming.

Prerequisites

Make sure you’ve completed the installation steps before continuing.

Build Your First Component

Let’s create a user profile form that showcases Stride’s components, accessibility features, and theming capabilities.
1

Import components

Start by importing the components you’ll need:
import {
  Button,
  Card,
  CardHeader,
  CardTitle,
  CardDescription,
  CardContent,
  CardFooter,
  Input,
  Switch,
  Badge,
} from 'stride-ds';
import 'stride-ds/styles';
All components are fully typed with TypeScript. Your IDE will provide autocomplete for props.
2

Create the form component

Build a simple profile form using Stride components:
ProfileForm.tsx
'use client'; // Required for Next.js App Router

import { useState } from 'react';
import {
  Button,
  Card,
  CardHeader,
  CardTitle,
  CardDescription,
  CardContent,
  CardFooter,
  Input,
  Switch,
  Badge,
} from 'stride-ds';

export function ProfileForm() {
  const [emailNotifications, setEmailNotifications] = useState(true);

  return (
    <Card variant="elevated" className="max-w-md">
      <CardHeader>
        <div className="flex items-center justify-between">
          <CardTitle>User Profile</CardTitle>
          <Badge variant="success">Active</Badge>
        </div>
        <CardDescription>
          Update your profile information and preferences
        </CardDescription>
      </CardHeader>

      <CardContent className="space-y-4">
        <Input
          label="Full Name"
          placeholder="Enter your name"
          isRequired
        />

        <Input
          label="Email Address"
          type="email"
          placeholder="[email protected]"
          isRequired
        />

        <Input
          label="Bio"
          placeholder="Tell us about yourself"
          helperText="Optional - shown on your public profile"
        />

        <div className="flex items-center justify-between py-2">
          <div>
            <label className="font-medium text-sm">
              Email Notifications
            </label>
            <p className="text-xs text-[var(--text-secondary)]">
              Receive updates about your account
            </p>
          </div>
          <Switch
            isSelected={emailNotifications}
            onChange={setEmailNotifications}
          />
        </div>
      </CardContent>

      <CardFooter>
        <Button variant="secondary">Cancel</Button>
        <Button variant="primary">Save Changes</Button>
      </CardFooter>
    </Card>
  );
}
Notice how we’re using semantic tokens like var(--text-secondary) for custom styling. This ensures your styles adapt to different brands.
3

Add brand theming (optional)

Apply a brand theme to your entire application:
App.tsx
import { BrandInitializer } from 'stride-ds';
import { ProfileForm } from './ProfileForm';

export default function App() {
  return (
    <>
      <BrandInitializer brand="coral" />
      <div className="min-h-screen bg-[var(--bg-primary)] p-8">
        <ProfileForm />
      </div>
    </>
  );
}
Available brands:
  • stride (default) - Modern blue theme
  • coral - Warm orange-red theme
  • forest - Natural green theme
  • runswap - Energetic purple theme
  • acme - Modern indigo tech theme
4

Run your application

Start your development server and see your form in action:
npm run dev
Your form is now live with full accessibility, keyboard navigation, and brand theming!

Complete Working Example

Here’s the full code for a production-ready profile page:
ProfilePage.tsx
'use client';

import { useState } from 'react';
import {
  Button,
  Card,
  CardHeader,
  CardTitle,
  CardDescription,
  CardContent,
  CardFooter,
  Input,
  Switch,
  Badge,
  Alert,
  BrandInitializer,
} from 'stride-ds';
import 'stride-ds/styles';

export default function ProfilePage() {
  const [emailNotifications, setEmailNotifications] = useState(true);
  const [fullName, setFullName] = useState('');
  const [email, setEmail] = useState('');
  const [bio, setBio] = useState('');
  const [saved, setSaved] = useState(false);

  const handleSave = () => {
    // Validate and save logic here
    setSaved(true);
    setTimeout(() => setSaved(false), 3000);
  };

  return (
    <>
      <BrandInitializer brand="stride" />
      
      <main className="min-h-screen bg-[var(--bg-secondary)] p-8">
        <div className="max-w-2xl mx-auto space-y-6">
          <div>
            <h1 className="text-3xl font-bold text-[var(--text-primary)]">
              Account Settings
            </h1>
            <p className="text-[var(--text-secondary)] mt-2">
              Manage your profile and notification preferences
            </p>
          </div>

          {saved && (
            <Alert variant="success" title="Success">
              Your profile has been updated successfully.
            </Alert>
          )}

          <Card variant="elevated">
            <CardHeader>
              <div className="flex items-center justify-between">
                <CardTitle>User Profile</CardTitle>
                <Badge variant="success">Active</Badge>
              </div>
              <CardDescription>
                Update your profile information and preferences
              </CardDescription>
            </CardHeader>

            <CardContent className="space-y-4">
              <Input
                label="Full Name"
                placeholder="Enter your name"
                value={fullName}
                onChange={(e) => setFullName(e.target.value)}
                isRequired
              />

              <Input
                label="Email Address"
                type="email"
                placeholder="[email protected]"
                value={email}
                onChange={(e) => setEmail(e.target.value)}
                isRequired
              />

              <Input
                label="Bio"
                placeholder="Tell us about yourself"
                value={bio}
                onChange={(e) => setBio(e.target.value)}
                helperText="Optional - shown on your public profile"
              />

              <div className="border-t border-[var(--border-primary)] pt-4" />

              <div className="flex items-center justify-between py-2">
                <div>
                  <label className="font-medium text-sm text-[var(--text-primary)]">
                    Email Notifications
                  </label>
                  <p className="text-xs text-[var(--text-secondary)] mt-1">
                    Receive updates about your account activity
                  </p>
                </div>
                <Switch
                  isSelected={emailNotifications}
                  onChange={setEmailNotifications}
                  aria-label="Toggle email notifications"
                />
              </div>
            </CardContent>

            <CardFooter>
              <Button variant="secondary" onPress={() => window.history.back()}>
                Cancel
              </Button>
              <Button 
                variant="primary" 
                onPress={handleSave}
                isDisabled={!fullName || !email}
              >
                Save Changes
              </Button>
            </CardFooter>
          </Card>
        </div>
      </main>
    </>
  );
}

Understanding the Code

Let’s break down the key concepts:

Component Composition

Stride components are designed to be composed together:
<Card>              {/* Container */}
  <CardHeader>      {/* Visual grouping */}
    <CardTitle />   {/* Semantic heading */}
  </CardHeader>
  <CardContent>     {/* Main content area */}
    <Input />       {/* Form fields */}
  </CardContent>
  <CardFooter>      {/* Actions */}
    <Button />      {/* Interactive elements */}
  </CardFooter>
</Card>

Semantic Design Tokens

Use CSS variables for custom styling:
// ✅ Good - Uses semantic tokens
<div className="bg-[var(--bg-primary)] text-[var(--text-primary)]" />

// ❌ Avoid - Hard-coded colors break theming
<div className="bg-white text-gray-900" />
Common tokens:
  • Text: --text-primary, --text-secondary, --text-tertiary
  • Background: --bg-primary, --bg-secondary, --bg-tertiary
  • Interactive: --interactive-primary, --interactive-primary-hover
  • Borders: --border-primary, --border-secondary, --border-focus
  • Status: --status-success, --status-warning, --status-danger

React Aria Integration

All components use React Aria for accessibility:
<Button onPress={() => console.log('clicked')}>
  {/* onPress works with clicks, keyboard, and touch */}
  Click me
</Button>

<Input 
  isRequired          {/* Adds ARIA required attribute */}
  isDisabled={false}  {/* Manages aria-disabled */}
  aria-label="Email"  {/* Custom ARIA labels supported */}
/>

Component Variants

Most components support variants for different styles:
{/* Button variants */}
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="destructive">Delete</Button>

{/* Card variants */}
<Card variant="outlined">Outlined</Card>
<Card variant="elevated">Elevated</Card>
<Card variant="interactive">Interactive</Card>

{/* Badge variants */}
<Badge variant="default">Default</Badge>
<Badge variant="success">Success</Badge>
<Badge variant="warning">Warning</Badge>
<Badge variant="danger">Danger</Badge>

Dynamic Brand Switching

You can change brands dynamically at runtime:
BrandSwitcher.tsx
'use client';

import { useState } from 'react';
import { applyBrandTheme } from 'stride-ds';
import { Button } from 'stride-ds';

export function BrandSwitcher() {
  const [currentBrand, setCurrentBrand] = useState('stride');

  const brands = [
    { id: 'stride', name: 'Stride', color: 'blue' },
    { id: 'coral', name: 'Coral', color: 'orange' },
    { id: 'forest', name: 'Forest', color: 'green' },
    { id: 'runswap', name: 'Runswap', color: 'purple' },
    { id: 'acme', name: 'Acme', color: 'indigo' },
  ];

  const switchBrand = (brandId: string) => {
    applyBrandTheme(brandId);
    setCurrentBrand(brandId);
  };

  return (
    <div className="flex gap-2">
      {brands.map((brand) => (
        <Button
          key={brand.id}
          variant={currentBrand === brand.id ? 'primary' : 'secondary'}
          size="sm"
          onPress={() => switchBrand(brand.id)}
        >
          {brand.name}
        </Button>
      ))}
    </div>
  );
}
Brand preferences are automatically saved to localStorage and persist across sessions.

Next Steps

Now that you’ve built your first component, explore more features:

Component Library

Explore all 18+ available components

Brand System

Create custom brands and themes

Design Tokens

Learn about the token system

Accessibility

Best practices for accessible UIs

TypeScript Guide

Advanced TypeScript patterns

Customization

Extend and customize components

Common Patterns

Use the errorMessage prop for inline validation:
<Input
  label="Email"
  type="email"
  value={email}
  onChange={(e) => setEmail(e.target.value)}
  errorMessage={!email.includes('@') ? 'Invalid email' : undefined}
/>
Disable buttons during async operations:
<Button 
  variant="primary"
  isDisabled={isLoading}
  onPress={async () => {
    setIsLoading(true);
    await saveProfile();
    setIsLoading(false);
  }}
>
  {isLoading ? 'Saving...' : 'Save'}
</Button>
Toggle dark mode by adding a class to the root element:
const toggleDarkMode = () => {
  document.documentElement.classList.toggle('dark');
};
All semantic tokens automatically adapt to dark mode.
Having issues? Check the installation guide or ask in our GitHub Discussions.

Build docs developers (and LLMs) love