Skip to main content

Build your first app

This guide will walk you through creating a simple application using Apsara components. You’ll learn how to use layouts, buttons, and theme customization.
Make sure you’ve installed Apsara before starting this guide.
1

Import the required components

Start by importing the components and styles you’ll need:
App.tsx
import "@raystack/apsara/style.css";
import { Button, Flex, ThemeProvider } from "@raystack/apsara";
The style.css import provides all the base styles for Apsara components.
2

Create a layout with Flex

Use the Flex component to create a flexible layout container:
App.tsx
function App() {
  return (
    <Flex
      direction="column"
      align="center"
      justify="center"
      gap="large"
    >
      <h1>Welcome to Apsara</h1>
    </Flex>
  );
}
The Flex component provides props for controlling flexbox layout:
  • direction: row, column, rowReverse, columnReverse
  • align: start, center, end, stretch, baseline
  • justify: start, center, end, between
  • gap: 1-9, extra-small, small, medium, large, extra-large
3

Add buttons with variants

Add buttons with different variants to your layout:
App.tsx
function App() {
  return (
    <Flex
      direction="column"
      align="center"
      justify="center"
      gap="large"
    >
      <h1>Welcome to Apsara</h1>
      
      <Flex gap="medium">
        <Button variant="solid" color="accent">
          Primary Action
        </Button>
        <Button variant="outline" color="neutral">
          Secondary
        </Button>
        <Button variant="ghost" color="danger">
          Delete
        </Button>
      </Flex>
    </Flex>
  );
}
Buttons support multiple variants and colors:
  • Variants: solid, outline, ghost, text
  • Colors: accent, neutral, danger, success
  • Sizes: small, normal
4

Add loading states and icons

Buttons can show loading states and include icons:
App.tsx
import { Button, Flex } from "@raystack/apsara";
import { PlusIcon } from "@raystack/apsara/icons";

function App() {
  const [loading, setLoading] = useState(false);

  const handleClick = () => {
    setLoading(true);
    setTimeout(() => setLoading(false), 2000);
  };

  return (
    <Flex gap="medium">
      <Button
        variant="solid"
        color="accent"
        loading={loading}
        loaderText="Saving..."
        onClick={handleClick}
      >
        Save Changes
      </Button>
      
      <Button
        variant="outline"
        leadingIcon={<PlusIcon />}
      >
        Add Item
      </Button>
    </Flex>
  );
}
Use loaderText to show text while loading, or omit it to show only the spinner.
5

Set up theme provider

Wrap your application with ThemeProvider to enable theming and dark mode:
App.tsx
import { ThemeProvider, ThemeSwitcher } from "@raystack/apsara";

function App() {
  return (
    <ThemeProvider
      defaultTheme="light"
      style="modern"
      accentColor="indigo"
      grayColor="gray"
    >
      <Flex direction="column" gap="large">
        <Flex justify="end">
          <ThemeSwitcher />
        </Flex>
        
        <Flex direction="column" align="center" gap="large">
          <h1>Welcome to Apsara</h1>
          
          <Flex gap="medium">
            <Button variant="solid" color="accent">
              Get Started
            </Button>
            <Button variant="outline" color="neutral">
              Learn More
            </Button>
          </Flex>
        </Flex>
      </Flex>
    </ThemeProvider>
  );
}
The ThemeProvider accepts several props:
  • defaultTheme: “light”, “dark”, or “system”
  • style: Theme style variant
  • accentColor: Primary accent color
  • grayColor: Gray scale variant
  • enableSystem: Enable system theme detection (default: true)

Complete example

Here’s a complete working example that combines everything:
App.tsx
import { useState } from "react";
import "@raystack/apsara/style.css";
import {
  Button,
  Flex,
  ThemeProvider,
  ThemeSwitcher,
  InputField,
} from "@raystack/apsara";

function App() {
  const [email, setEmail] = useState("");
  const [loading, setLoading] = useState(false);

  const handleSubmit = () => {
    setLoading(true);
    // Simulate API call
    setTimeout(() => {
      console.log("Email submitted:", email);
      setLoading(false);
    }, 2000);
  };

  return (
    <ThemeProvider defaultTheme="system" accentColor="indigo">
      <div style={{ minHeight: "100vh", padding: "2rem" }}>
        <Flex direction="column" gap="large">
          {/* Header with theme switcher */}
          <Flex justify="between" align="center">
            <h1>My Apsara App</h1>
            <ThemeSwitcher />
          </Flex>

          {/* Main content */}
          <Flex direction="column" align="center" gap="large">
            <div style={{ maxWidth: "400px", width: "100%" }}>
              <Flex direction="column" gap="medium">
                <InputField
                  type="email"
                  placeholder="Enter your email"
                  value={email}
                  onChange={(e) => setEmail(e.target.value)}
                />
                
                <Flex gap="medium">
                  <Button
                    variant="solid"
                    color="accent"
                    loading={loading}
                    loaderText="Submitting..."
                    onClick={handleSubmit}
                    disabled={!email}
                  >
                    Submit
                  </Button>
                  <Button
                    variant="outline"
                    color="neutral"
                    onClick={() => setEmail("")}
                  >
                    Clear
                  </Button>
                </Flex>
              </Flex>
            </div>
          </Flex>
        </Flex>
      </div>
    </ThemeProvider>
  );
}

export default App;

Using the useTheme hook

Access and control the theme programmatically using the useTheme hook:
import { useTheme } from "@raystack/apsara";

function MyComponent() {
  const { theme, setTheme, resolvedTheme } = useTheme();

  return (
    <div>
      <p>Current theme: {theme}</p>
      <p>Resolved theme: {resolvedTheme}</p>
      
      <Button onClick={() => setTheme("dark")}>
        Switch to Dark
      </Button>
      <Button onClick={() => setTheme("light")}>
        Switch to Light
      </Button>
      <Button onClick={() => setTheme("system")}>
        Use System
      </Button>
    </div>
  );
}
The useTheme hook returns:
  • theme: Current theme setting (“light”, “dark”, or “system”)
  • setTheme: Function to change the theme
  • resolvedTheme: Actual theme being used (resolves “system” to “light” or “dark”)
  • systemTheme: The system’s preferred theme
  • themes: Array of available themes
Use resolvedTheme when you need to know the actual theme being displayed, especially when using “system” mode.

Layout patterns

Here are some common layout patterns using the Flex component:
<Flex direction="row" gap="medium" align="center">
  <Button>First</Button>
  <Button>Second</Button>
  <Button>Third</Button>
</Flex>

Next steps

Now that you’ve built your first app with Apsara, explore more features:

Components

Explore all 55+ available components

Theming

Learn how to customize colors and styles

Dark mode

Implement dark mode in your app

Accessibility

Learn about accessibility features
All Apsara components are accessible by default and follow ARIA best practices.