Skip to main content

Quick Start

This guide will walk you through creating your first interactive UI with Svelte Atoms Core. We’ll build a complete form with a dialog, demonstrating the library’s composability and reactivity.

What We’ll Build

We’ll create a user registration form with:
  • Input fields for name and email
  • A button to open a confirmation dialog
  • A dialog with header, body, and footer sections
  • Reactive state management using Svelte 5 runes
1

Create a New Component

Create a new file called RegistrationForm.svelte in your project:
RegistrationForm.svelte
<script lang="ts">
  import { Button, Dialog, Input } from '@svelte-atoms/core';

  let dialogOpen = $state(false);
  let name = $state('');
  let email = $state('');

  function handleSubmit() {
    if (name && email) {
      dialogOpen = true;
    }
  }

  function handleConfirm() {
    console.log('User registered:', { name, email });
    dialogOpen = false;
    // Reset form
    name = '';
    email = '';
  }
</script>
Notice we’re using Svelte 5’s $state rune for reactive state. This is the recommended way to manage state in Svelte 5.
2

Build the Form

Add the form structure with input fields:
RegistrationForm.svelte
<div class="max-w-md mx-auto p-6 space-y-4">
  <h2 class="text-2xl font-bold">Register</h2>

  <div class="space-y-2">
    <label for="name" class="block text-sm font-medium">
      Name
    </label>
    <Input.Root>
      <Input.Control
        id="name"
        bind:value={name}
        placeholder="Enter your name"
        type="text"
      />
    </Input.Root>
  </div>

  <div class="space-y-2">
    <label for="email" class="block text-sm font-medium">
      Email
    </label>
    <Input.Root>
      <Input.Control
        id="email"
        bind:value={email}
        placeholder="Enter your email"
        type="email"
      />
    </Input.Root>
  </div>

  <Button onclick={handleSubmit}>Submit</Button>
</div>
The Input component is composed of Input.Root and Input.Control. This pattern allows for flexible composition with icons, placeholders, and other elements.
3

Add the Confirmation Dialog

Add a dialog that appears after form submission:
RegistrationForm.svelte
<Dialog.Root bind:open={dialogOpen}>
  <Dialog.Content>
    <Dialog.Header>
      <Dialog.Title>Confirm Registration</Dialog.Title>
    </Dialog.Header>
    
    <Dialog.Body>
      <p class="mb-2">Please confirm your details:</p>
      <dl class="space-y-1">
        <div>
          <dt class="font-semibold">Name:</dt>
          <dd class="text-gray-600">{name}</dd>
        </div>
        <div>
          <dt class="font-semibold">Email:</dt>
          <dd class="text-gray-600">{email}</dd>
        </div>
      </dl>
    </Dialog.Body>
    
    <Dialog.Footer>
      <Button onclick={() => (dialogOpen = false)}>Cancel</Button>
      <Button variant="primary" onclick={handleConfirm}>Confirm</Button>
    </Dialog.Footer>
  </Dialog.Content>
</Dialog.Root>
The Dialog.Root component manages the dialog state. By binding to the open prop, we can control the dialog from our component logic.
4

Complete Component Code

Here’s the complete component code:
RegistrationForm.svelte
<script lang="ts">
  import { Button, Dialog, Input } from '@svelte-atoms/core';

  let dialogOpen = $state(false);
  let name = $state('');
  let email = $state('');

  function handleSubmit() {
    if (name && email) {
      dialogOpen = true;
    }
  }

  function handleConfirm() {
    console.log('User registered:', { name, email });
    dialogOpen = false;
    name = '';
    email = '';
  }
</script>

<div class="max-w-md mx-auto p-6 space-y-4">
  <h2 class="text-2xl font-bold">Register</h2>

  <div class="space-y-2">
    <label for="name" class="block text-sm font-medium">
      Name
    </label>
    <Input.Root>
      <Input.Control
        id="name"
        bind:value={name}
        placeholder="Enter your name"
        type="text"
      />
    </Input.Root>
  </div>

  <div class="space-y-2">
    <label for="email" class="block text-sm font-medium">
      Email
    </label>
    <Input.Root>
      <Input.Control
        id="email"
        bind:value={email}
        placeholder="Enter your email"
        type="email"
      />
    </Input.Root>
  </div>

  <Button onclick={handleSubmit}>Submit</Button>
</div>

<Dialog.Root bind:open={dialogOpen}>
  <Dialog.Content>
    <Dialog.Header>
      <Dialog.Title>Confirm Registration</Dialog.Title>
    </Dialog.Header>
    
    <Dialog.Body>
      <p class="mb-2">Please confirm your details:</p>
      <dl class="space-y-1">
        <div>
          <dt class="font-semibold">Name:</dt>
          <dd class="text-gray-600">{name}</dd>
        </div>
        <div>
          <dt class="font-semibold">Email:</dt>
          <dd class="text-gray-600">{email}</dd>
        </div>
      </dl>
    </Dialog.Body>
    
    <Dialog.Footer>
      <Button onclick={() => (dialogOpen = false)}>Cancel</Button>
      <Button variant="primary" onclick={handleConfirm}>Confirm</Button>
    </Dialog.Footer>
  </Dialog.Content>
</Dialog.Root>

Advanced Example: Searchable Dropdown

Let’s create a more advanced component - a multi-select dropdown with search functionality:
FruitSelector.svelte
<script lang="ts">
  import { Dropdown, Input, filter } from '@svelte-atoms/core';
  import { flip } from 'svelte/animate';

  let data = [
    { id: 1, value: 'apple', text: 'Apple' },
    { id: 2, value: 'banana', text: 'Banana' },
    { id: 3, value: 'cherry', text: 'Cherry' },
    { id: 4, value: 'date', text: 'Date' },
    { id: 5, value: 'elderberry', text: 'Elderberry' }
  ];

  let open = $state(false);
  
  // Use the filter helper for search functionality
  const dd = filter(
    () => data,
    (query, item) => item.text.toLowerCase().includes(query.toLowerCase())
  );
</script>

<Dropdown.Root
  bind:open
  multiple
  keys={data.map((item) => item.value)}
  onquerychange={(q) => (dd.query = q)}
>
  {#snippet children({ dropdown })}
    <!-- Compose Dropdown.Trigger with Input.Root -->
    <Dropdown.Trigger
      base={Input.Root}
      onclick={(ev) => {
        ev.preventDefault();
        dropdown.state.open();
      }}
    >
      {#each dropdown?.state?.selectedItems ?? [] as item (item.id)}
        <div animate:flip={{ duration: 200 }}>
          <Dropdown.Value value={item.value}>{item.text}</Dropdown.Value>
        </div>
      {/each}
      <Dropdown.Query placeholder="Search fruits..." />
    </Dropdown.Trigger>

    <Dropdown.Content>
      {#each dd.current as item (item.id)}
        <div animate:flip={{ duration: 200 }}>
          <Dropdown.Item value={item.value}>{item.text}</Dropdown.Item>
        </div>
      {/each}
    </Dropdown.Content>
  {/snippet}
</Dropdown.Root>
This example demonstrates:
  • Composition using the base prop to combine Input and Dropdown
  • Snippets for flexible content rendering
  • Animation using Svelte’s flip animation
  • Filter helper for reactive search functionality

Key Concepts

Composition with base

The base prop allows you to use one component as the foundation for another:
<!-- Button as Popover trigger -->
<Popover.Trigger base={Button} variant="outline">
  Open Popover
</Popover.Trigger>

<!-- Input as Dropdown trigger -->
<Dropdown.Trigger base={Input.Root}>
  <Input.Control placeholder="Select..." />
</Dropdown.Trigger>

Component Structure

Many components follow a Root/Content pattern:
<Component.Root>
  <Component.Trigger>Open</Component.Trigger>
  <Component.Content>
    <!-- Content here -->
  </Component.Content>
</Component.Root>

Reactive State with Runes

Use Svelte 5 runes for reactive state:
<script lang="ts">
  let value = $state('');           // Reactive state
  let doubled = $derived(value * 2); // Derived state
  
  $effect(() => {
    // Run side effects when dependencies change
    console.log('Value changed:', value);
  });
</script>

Next Steps

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

Component Library

Explore all 30+ components with examples

Styling Guide

Learn how to customize component styles

Composition Patterns

Master advanced composition techniques

API Reference

Deep dive into component APIs and props

Need Help?

View Examples

Check out the Storybook for interactive examples of every component

Build docs developers (and LLMs) love