# Get Started
This guide provides the essentials for adding **audio/ui** components to your React application.
## Prerequisites
Our components are built with [Tailwind CSS v4](https://tailwindcss.com). Before you begin, make sure you have a React project set up with Tailwind CSS.
## Adding Components
You can add components **automatically with the shadcn CLI** or **manually by copying the files**.
#### With the CLI
Each component page provides a command to add it to your project automatically. The CLI will create the necessary files and install any dependencies for you.
For example, to add the audio player:
```bash
npx shadcn@latest add @audio/player
```
#### Manually
1. **Find a component** on the [Components](/docs) or [Particles](/particles) pages.
2. **Copy the code** from the **Code** tab.
3. **Create a new file** in your project (e.g., `components/ui/audio/player.tsx`) and paste the code.
4. **Install any dependencies** listed on the component's page.
5. **Import and use** the component in your app.
### Step 3: Setup AudioProvider
Provide the audio context at the root. You can copy the provider source directly or use the usage snippet below.
Usage
Code
```tsx
// app/layout.tsx or _app.tsx
import { AudioProvider } from "@/components/audio/provider"
export default function RootLayout({ children }) {
return (
{children}
)
}
```
### Step 4: Use the component
```tsx
import { AudioPlayer, AudioPlayerControlBar, AudioPlayerPlay } from "@/components/audio/player"
export function MyAudioPlayer() {
return (
)
}
```
## Styling
Components are styled with a design token system defined by CSS variables and implemented with Tailwind CSS. The variables follow the same approach as shadcn/ui and are fully customizable.
For detailed information about styling, color tokens, and customization options, see the [shadcn/ui theming documentation](https://ui.shadcn.com/docs/theming).
## Working with LLMs
We structure the documentation to make the components **AI-friendly**, so language models can understand, reason about, and modify them. To support this, we include:
* A [llms.txt](/llms.txt) file that provides a map of the documentation and component structure for your AI agent.
* A [llms-full.txt](/llms-full.txt) file containing an expanded view of the docs and component sources for deeper analysis.
* A **Copy Markdown** button on every page, so you can easily share content or feed it to your AI workflows.
# Introduction
**audio/ui** is a collection of beautifully designed, accessible, and composable components for building audio interfaces in your React apps. Built on top of [shadcn/ui](https://ui.shadcn.com/) and styled with [Tailwind CSS](https://tailwindcss.com/), it's designed for you to copy, paste, and own.
**This is not a component library. It is how you build your audio component library.**
You know how most traditional component libraries work: you install a package from NPM, import the components, and use them in your app.
This approach works well until you need to customize a component to fit your design system or require one that isn't included in the library. **Often, you end up wrapping library components, writing workarounds to override styles, or mixing components from different libraries with incompatible APIs.**
This is what audio/ui aims to solve. It is built around the following principles:
## Open Code
audio/ui hands you the actual component code. You have full control to customize and extend the components to your needs. This means:
* **Full Transparency:** You see exactly how each component is built.
* **Easy Customization:** Modify any part of a component to fit your design and functionality requirements.
* **AI Integration:** Access to the code makes it straightforward for LLMs to read, understand, and even improve your components.
*In a typical library, if you need to change a button's behavior, you have to override styles or wrap the component. With audio/ui, you simply edit the button code directly.*
## Composition
Every component in audio/ui shares a common, composable interface. **If a component does not exist, we bring it in, make it composable, and adjust its style to match and work with the rest of the design system.**
*A shared, composable interface means it's predictable for both your team and LLMs. You are not learning different APIs for every new component. Even for third-party ones.*
## Beautiful Defaults
audio/ui comes with a collection of components that have carefully chosen default styles. They are designed to look good on their own and to work well together as a consistent system:
* **Good Out-of-the-Box:** Your UI has a clean and minimal look without extra work.
* **Unified Design:** Components naturally fit with one another. Each component is built to match the others, keeping your UI consistent.
* **Easily Customizable:** If you want to change something, it's simple to override and extend the defaults.
## AI-Ready
The design of audio/ui makes it easy for AI tools to work with your code. Its open code and consistent API allow AI models to read, understand, and even generate new components.
*An AI model can learn how your components work and suggest improvements or even create new components that integrate with your existing design.*
## Particles
We provide [particles](/particles)—pre-assembled components that combine multiple primitives into ready-to-use solutions. They're easy to customize, extend, or break apart when needed.
## Open Source
This project is open source. We welcome contributions, feedback, or improvements. Check out our [repository](https://github.com/ouestlabs/audio-ui) on GitHub.
# Roadmap
**audio/ui** is a modern component library for building audio interfaces in React applications. Built on top of [shadcn/ui](https://ui.shadcn.com/), we provide accessible, composable components that you can copy, paste, and customize to fit your needs.
## Current Status
We're actively building and improving `audio/ui`. Here's what's available today:
### Components
* **Audio Player**: A fully-featured, composable audio player with queue management, shuffle, repeat modes, and volume control
* **Audio Queue**: Queue management components with search, sortable tracks, and preferences
* **Custom UI Components**: Extended components like `slider` (with buffer support) and `empty` that complement shadcn/ui's component set
### Library
* **Audio Store**: A Zustand-based state management solution for audio playback with localStorage persistence
* **Audio Utilities**: Helper functions for formatting durations and managing audio playback
### Particles
* **Pre-assembled Patterns**: Ready-to-use audio player patterns that combine multiple components
## What's Next
We're continuously working on improving and expanding `audio/ui`. Here's what we're planning:
### Components
* **More Audio Components**: Additional audio-specific UI patterns and controls
* **Enhanced Player Features**: Advanced playback controls, playlist management, and audio visualization
* **Visualization Components**: Waveform displays, spectrum analyzers, and other audio visualizations
### Documentation
* **More Examples**: Additional usage examples and patterns
* **Guides**: Best practices and advanced usage guides
* **API Reference**: Comprehensive API documentation
### Performance & Quality
* **Performance Optimizations**: Further improvements to component performance and bundle size
* **Accessibility Enhancements**: Continued focus on making all components fully accessible
* **TypeScript Improvements**: Enhanced type safety and better developer experience
## Contributing
We're building this in the open and welcome contributions! Whether it's bug reports, feature requests, or code contributions, we'd love to have you involved.
Check out our [GitHub repository](https://github.com/ouestlabs/audio-ui) to get started.
# Components
# Audio Player
A fully composable audio player component system. Build custom audio interfaces by combining individual control components.
## Installation
CLI
Manual
```bash
npx shadcn@latest add @audio/player
```
Install the runtime dependencies:
```bash
npm install zustand lucide-react
```
Install the required shadcn/ui primitives:
```bash
npx shadcn@latest add button dialog dropdown-menu empty input item scroll-area toggle tooltip
```
Install the slider components from this registry:
```bash
npx shadcn@latest add @audio/slider
```
Copy and paste the following code into your project.
Copy the required provider file.
Copy the audio lib file.
Copy the required store file.
Update the import paths to match your project setup.
## Setup
AudioProvider Required: Wrap your app with AudioProvider at the root level. This provides the audio context to all components.
First, wrap your app with `AudioProvider` at the root level:
```tsx showLineNumbers
// app/layout.tsx or _app.tsx
import { AudioProvider } from "@/components/audio/provider"
export default function RootLayout({ children }) {
return (
{children}
)
}
```
## Usage
Import the components you need:
```tsx showLineNumbers
import {
AudioPlayer,
AudioPlayerControlBar,
AudioPlayerControlGroup,
AudioPlayerPlay,
AudioPlayerSkipBack,
AudioPlayerSkipForward,
AudioPlayerRewind,
AudioPlayerFastForward,
AudioPlayerSeekBar,
AudioPlayerTimeDisplay,
AudioPlayerVolume,
} from "@/components/audio/player"
```
```tsx
```
### Stacked Layout
A player with stacked variant, showing time displays above and below the seek bar.
### Player with Queue
A full-featured player with queue management, shuffle, and repeat controls. Uses `AudioPlayerControlGroup` for flexible layout management.
For more queue management options, see the [Audio Queue documentation](/docs/components/queue).
## API Reference
### AudioPlayer
A styled container component for audio controls. This is a presentational wrapper that provides consistent styling. All audio components are self-contained and read state from the global store.
#### Props
Inherits all props from `HTMLDivElement`.
#### Example
```tsx
{/* Your audio controls */}
```
### AudioPlayerControlBar
A container component that holds audio player controls. Use it to wrap your control components.
#### Props
| Prop | Type | Default | Description |
| ----------- | ------------------------ | ----------- | ----------------------------------- |
| `variant` | `"compact" \| "stacked"` | `"compact"` | Layout variant for the control bar. |
| `className` | `string` | - | Additional CSS classes. |
#### Example
```tsx
```
#### Stacked Layout
```tsx
```
### AudioPlayerControlGroup
A flexible wrapper component for grouping audio controls and managing flex layouts. Useful for creating custom layouts with `flex-col`, `justify-between`, etc.
#### Props
| Prop | Type | Default | Description |
| ----------- | -------- | ------- | ----------------------- |
| `className` | `string` | - | Additional CSS classes. |
#### Example
```tsx showLineNumbers
```
### AudioPlayerPlay
A button that toggles play/pause state. Shows a loading spinner when buffering or loading, and automatically handles keyboard shortcuts (Space bar to play/pause).
Inherits all props from `AudioPlayerButton` (which extends shadcn/ui's `Button` component).
Keyboard Shortcuts: The AudioPlayerPlay component automatically handles the Space bar keyboard shortcut for play/pause.
This works when the focus is on the document body.
### AudioPlayerSkipBack
A button that navigates to the previous track.
Inherits all props from `AudioPlayerButton` (which extends shadcn/ui's `Button` component).
### AudioPlayerSkipForward
A button that navigates to the next track.
Inherits all props from `AudioPlayerButton` (which extends shadcn/ui's `Button` component).
### AudioPlayerRewind
A button that seeks backward by 10 seconds.
Inherits all props from `AudioPlayerButton` (which extends shadcn/ui's `Button` component).
### AudioPlayerFastForward
A button that seeks forward by 10 seconds.
Inherits all props from `AudioPlayerButton` (which extends shadcn/ui's `Button` component).
### AudioPlayerSeekBar
A slider component that shows playback progress and allows seeking. Automatically syncs with the current track's playback position.
Inherits all props from `Slider` component (except `value`, `onValueChange`, `min`, `max`, `bufferValue`).
### AudioPlayerTimeDisplay
Displays the current playback time or remaining time.
#### Props
| Prop | Type | Default | Description |
| ----------- | --------- | ------- | ----------------------------------------------- |
| `remaining` | `boolean` | `false` | Display remaining time instead of current time. |
| `className` | `string` | - | Additional CSS classes. |
### AudioPlayerVolume
A dropdown menu button with a slider to control volume. Click to open the volume control menu.
Inherits all props from `DropdownMenu` component.
## Notes
Important Information:
* **Component Architecture:** All audio components are self-contained and read state directly from the global store. The audio player uses the HTML5 audio element under the hood.
* **Previous Button Behavior:** The previous button restarts the track if it has been played for more than 3 seconds, otherwise it navigates to the previous track in the queue.
Live Stream Limitations: Live streams disable seeking, rewind, and fast-forward controls. The seek bar is disabled and shows 100% progress for live streams.
# Audio Queue
A comprehensive queue management system. Includes shuffle, repeat modes, track selection, search, and drag-and-drop reordering.
## Installation
CLI
Manual
```bash
npx shadcn@latest add @audio/queue
```
Install the runtime dependencies:
```bash
npm install zustand lucide-react
```
Install the required shadcn/ui primitives:
```bash
npx shadcn@latest add button dialog dropdown-menu input empty item scroll-area toggle tooltip
```
Install the track component used by the queue:
```bash
npx shadcn@latest add @audio/track
```
Copy and paste the following code into your project.
Copy the required store file.
Copy the audio lib file.
Update the import paths to match your project setup.
## Usage
Import the components you need:
```tsx showLineNumbers
import {
AudioQueue,
AudioQueueButton,
AudioQueuePreferences,
AudioQueueRepeatMode,
AudioQueueShuffle,
} from "@/components/audio/queue"
```
### Basic Queue
The `AudioQueue` component opens a dialog showing the current queue with search functionality and track selection.
```tsx
```
### Queue with Shuffle and Repeat
Use this when you want quick access to shuffle and repeat controls. The toggle buttons provide fast switching between modes.
```tsx
```
### Queue with Preferences
Use this when you want a compact interface with all queue settings in a dropdown menu. Ideal for space-constrained layouts.
```tsx
```
### Queue with All Controls
Use this when you want both quick access buttons and a preferences menu. Provides the most comprehensive queue management interface.
```tsx
```
## API Reference
### AudioQueue
A dialog component that displays the current queue and allows track selection, search, and reordering.
#### Props
| Prop | Type | Default | Description |
| ------------------- | ------------------------- | --------------------------------------- | -------------------------------------------------------------------------------------- |
| `onTrackSelect` | `(index: number) => void` | - | Callback function to handle track selection. Receives the index of the selected track. |
| `searchPlaceholder` | `string` | `"Search for a track..."` | Placeholder text for the search input. |
| `emptyLabel` | `string` | `"No tracks found"` | Label to display when the queue is empty. |
| `emptyDescription` | `string` | `"Try searching for a different track"` | Description to display when the queue is empty. |
#### Features
* **Search**: Filter tracks by title or artist
* **Track selection**: Click to play or pause current track
* **Drag and drop**: Reorder tracks when not searching
* **Remove tracks**: Remove individual tracks from queue
* **Clear queue**: Button to clear all tracks
Search and Drag-and-Drop: Search functionality filters tracks by title or artist. Track reordering is automatically disabled when search is active to prevent confusion.
The queue dialog automatically closes when a track is selected.
#### Example
```tsx
{
console.log("Selected track at index:", index);
}}
searchPlaceholder="Search tracks..."
emptyLabel="Queue is empty"
emptyDescription="Add tracks to the queue to see them here"
/>
```
### AudioQueueButton
A button component with optional tooltip support, used internally by queue components.
#### Props
| Prop | Type | Default | Description |
| -------------- | --------- | ------- | -------------------------- |
| `tooltip` | `boolean` | `false` | Whether to show a tooltip. |
| `tooltipLabel` | `string` | - | Tooltip text to display. |
#### Example
```tsx
```
### AudioQueueRepeatMode
A toggle button that cycles through repeat modes: none, one, all.
#### Props
Inherits all props from `Toggle` component.
#### Behavior
* **None**: No repeat (default)
* **One**: Repeat current track indefinitely
* **All**: Repeat entire queue
The button shows different icons based on the current mode:
* `RepeatIcon` for "all" mode
* `Repeat1Icon` for "one" mode
* No icon when disabled (none mode)
#### Example
```tsx
```
### AudioQueueShuffle
A toggle button that enables/disables shuffle mode. When enabled, the queue is shuffled randomly.
#### Props
Inherits all props from `Toggle` component (except `onPressedChange`).
#### Behavior
* When **enabled**: Queue is shuffled randomly
* When **disabled**: Queue maintains original order
The shuffle state persists until manually disabled.
State Persistence: The shuffle and repeat mode states are persisted to localStorage and will be restored when the app reloads.
#### Example
```tsx
```
### AudioQueuePreferences
A dropdown menu that combines repeat mode and insert mode controls in a compact interface.
#### Props
Inherits all props from `AudioQueueButton` component.
#### Features
* **Repeat mode selection**: None, One, All
* **Insert mode selection**: First, Last, After Current
#### Insert Modes
* **First**: New tracks are added at the beginning of the queue
* **Last**: New tracks are added at the end of the queue (default)
* **After Current**: New tracks are added after the currently playing track
Insert Mode Behavior: The insert mode determines where new tracks are added to the queue when using addToQueue.
This setting persists across sessions via localStorage.
#### Example
```tsx
```
## Integration with AudioPlayer
Queue components are designed to work seamlessly with the `AudioPlayer` component:
```tsx
```
## Notes
AudioProvider Requirement:AudioProvider (or the project's audio store) is required for queue components to work correctly.
All components read from and update the global audio store state.
# Audio Track
Track components for displaying and managing audio tracks. These components support two modes:
* Store mode: reads from and controls the global audio queue provided by the project's audio store.
* Controlled mode: accepts an explicit list or single track for use in search results or external lists.
## Installation
CLI
Manual
```bash
npx shadcn@latest add @audio/track
```
Install the runtime dependencies:
```bash
npm install zustand lucide-react class-variance-authority
```
Install the required shadcn/ui primitives:
```bash
npx shadcn@latest add avatar badge button item scroll-area empty
```
Install the sortable helper used by the examples:
```bash
npx shadcn@latest add @audio/sortable-list
```
Copy and paste the following code into your project.
Copy the required provider file.
Copy the required store file.
Copy the audio lib file.
Update the import paths to match your project setup.
## Usage
```tsx
import { AudioTrack, AudioTrackList } from "@/components/audio/track";
```
### AudioTrack
Displays a single track with optional cover, metadata and playback controls. Use either:
* `trackId` to render an item from the project's audio queue, or
* `track` to render a provided track object.
```tsx
// From queue by id
// With explicit data
handlePlay(customTrack)} />
```
### AudioTrackList
Renders a list of tracks. Pass `tracks` for a controlled list, otherwise the component renders the project queue.
Key features:
* Text filtering via `filterQuery` or a custom `filterFn`.
* Optional drag-and-drop (`sortable`) — reordering updates the queue only when not filtered and when `tracks` is not provided.
* Per-item remove action and play/pause controls (configurable via props).
```tsx
// Default: renders project queue
console.log(index)} />
// Controlled: render custom set
```
#### Grid Layout
#### Sortable
#### Sortable Grid
## API Reference
### AudioTrack
#### Props
| Prop | Type | Default | Description |
| ---------------- | --------------------------- | ------- | ------------------------------------------------------------------------------------- |
| `trackId` | `string \| number` | - | Load a track from the global queue (store mode). Do not use together with `track`. |
| `track` | `Track` | - | Render a provided track object (controlled mode). Do not use together with `trackId`. |
| `index` | `number` | - | Display index (one-based when shown). |
| `onClick` | `() => void` | - | Click handler invoked when the item is clicked. |
| `onRemove` | `(trackId: string) => void` | - | Called when the remove action is used. Receives the track id as string. |
| `showRemove` | `boolean` | `false` | Whether to show the remove (X) button. Hidden for the current playing track. |
| `showPlayPause` | `boolean` | `true` | Show play / pause control. |
| `showDragHandle` | `boolean` | `false` | Show a drag handle (used together with sortable lists). |
| `showCover` | `boolean` | `true` | Show album artwork (falls back to default icon). |
| `className` | `string` | - | Additional CSS classes. |
Important Information:
* **Operating Modes:** Use either trackId (store mode) or track (controlled mode). Both should not be used together. Store mode requires AudioProvider to be set up.
* **Track Display:** Cover images are taken from track.artwork or track.images\[0]. Live tracks show a "Live" badge and the duration is hidden.
### AudioTrackList
#### Props
| Prop | Type | Default | Description |
| ------------------ | ---------------------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tracks` | `Track[]` | - | Controlled list of tracks. When omitted the component reads from the global queue. |
| `onTrackSelect` | `(index: number, track?: Track) => void` | - | Called when a track is selected or played. In store mode the index is the queue index. In controlled mode the index refers to the provided `tracks` array. |
| `onTrackRemove` | `(trackId: string) => void` | - | Remove handler used by the per-item remove button. |
| `sortable` | `boolean` | `false` | Enable drag-and-drop reordering. Reordering will update the store queue only when not filtered and when `tracks` is not provided. |
| `showCover` | `boolean` | `true` | Show track cover image. |
| `variant` | `"default" \| "grid"` | `"default"` | Layout variant: stacked list or responsive grid. |
| `filterQuery` | `string` | - | Simple text filter (matches title or artist). |
| `filterFn` | `(track: Track) => boolean` | - | Custom filter function. When provided, `filterQuery` is ignored. |
| `emptyLabel` | `string` | `"No tracks found"` | Label shown when the list is empty. |
| `emptyDescription` | `string` | `"Try adding some tracks"` | Description shown in the empty state. |
| `className` | `string` | - | Additional CSS classes. |
Store vs Controlled Mode: When tracks is omitted, the component reads from the global queue (store mode).
When tracks is provided, it operates in controlled mode using the provided array.
Sortable Behavior: When sortable is enabled, reordering will update the store queue only when:
* The list is not filtered (filterQuery and filterFn are not used)
* The component is in store mode (tracks prop is not provided)
Keep sortable disabled while filtering to avoid confusing reordering behavior.
## Examples
### Store mode (main queue)
```tsx
console.log('play queue index', i)} />
```
### Controlled mode (search results)
```tsx
addToQueue(track)}
/>
```
### Filtering
```tsx
// or
t.genre === 'jazz'} />
```
### With removal
```tsx
removeFromQueue(id)} />
```
## Notes
Important Information:
* **AudioProvider Requirement:** AudioProvider (or the project's audio store) is required for store mode to work correctly. Make sure to wrap your app with the provider at the root level.
* **Localization:** If you localize strings (e.g., empty labels), pass emptyLabel and emptyDescription to the list component.
# Audio Store
## Installation
### Dependencies
The store uses Zustand. Install it if your app doesn't already include it:
```bash
npm install zustand
```
### Import
Import the store hook and types:
```tsx
import {
useAudioStore,
calculateNextIndex,
calculatePreviousIndex,
canUseDOM,
type AudioStore,
type RepeatMode,
type InsertMode,
} from "@/lib/audio-store"
```
## Core Concepts
### useAudioStore Hook
Performance Best Practices: Use granular selectors for better performance. Subscribe only to the specific slices of state you need.
This prevents unnecessary re-renders when unrelated state changes.
Access the store with granular selectors for better performance:
```tsx
const currentTime = useAudioStore((s) => s.currentTime)
const isPlaying = useAudioStore((s) => s.isPlaying)
```
For multiple values, extract them inside your component:
```tsx
function PlayerStatus() {
const currentTrack = useAudioStore((s) => s.currentTrack)
const duration = useAudioStore((s) => s.duration)
const isPlaying = useAudioStore((s) => s.isPlaying)
return (
)
}
```
### Direct Store Access
```tsx
import { useAudioStore } from "@/lib/audio-store"
// Read state without React subscriptions
const state = useAudioStore.getState()
console.log(state.isPlaying)
// Call actions directly
await useAudioStore.getState().play()
// Subscribe to changes (returns unsubscribe function)
const unsubscribe = useAudioStore.subscribe(
(s) => s.isPlaying,
(isPlaying) => console.log("Playing:", isPlaying)
)
```
## Persistence
The store automatically persists a subset of state to localStorage using Zustand's `persist` middleware:
| Category | Properties |
| ------------ | ----------------------------------------------------------------- |
| **Playback** | `currentTrack`, `currentTime`, `currentQueueIndex` |
| **Queue** | `queue`, `history` |
| **Settings** | `volume`, `isMuted`, `repeatMode`, `shuffleEnabled`, `insertMode` |
**Storage Key:** `audio:ui:store`
This allows users to resume playback and maintain queue state across page refreshes.
## Related
* [Audio Library](/docs/lib/audio) — Core audio playback singleton
* [Audio Player](/docs/components/player) — Player UI components
* [Audio Queue](/docs/components/queue) — Queue management UI
## Notes
Best Practices:
* Prefer selector subscriptions for performance (subscribe to specific slices of state)
* Async actions (play, next, setCurrentTrack) wait for audio loading
* addToQueue supports "first", "last", and "after" insert modes
* Queue shuffling randomizes order but preserves track identity
* setQueueAndPlay and similar actions trigger audio loading via the [Audio library](/docs/lib/audio)
* Persistence is automatic; no manual save required
* Direct store access via getState() is useful for non-React code or imperative operations
# Audio
The `$audio` singleton manages playback of HTML5 audio with automatic retry logic, event handling, and volume fading. Use it alongside the [Audio Store](/docs/lib/audio-store) for full player functionality.
## Installation
Import the singleton and helpers from the audio library:
```tsx
import { $audio, formatDuration, isLive, type Track } from "@/lib/audio"
```
## Core API
### $audio (Singleton)
Manages the underlying `HTMLAudioElement`, playback state, retries, and events. Initialize on client start — the instance is server-safe.
```tsx
import { $audio } from "@/lib/audio"
// Initialize on the client
$audio.init()
// Load and play
await $audio.load("https://example.com/audio.mp3", 0)
await $audio.play()
```
Client initialization: The $audio singleton must be initialized on the client. Call
$audio.init() from a client component or inside
useEffect() so the underlying HTMLAudioElement is
created only in the browser environment.
#### Lifecycle
| Method | Description |
| ----------- | ------------------------------------------------------- |
| `init()` | Initialize on the client. Safe to call multiple times. |
| `cleanup()` | Reset and release the audio element (pause, clear src). |
#### Playback
| Method | Description |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `load(url, startTime?, isLive?)` | Load an audio source and wait for readiness. Pass `isLive` for live streams (longer timeout, no seek). Returns `Promise`. |
| `play()` | Start or resume playback. Returns promise that resolves when browser allows playback. |
Browser autoplay restrictions: The play() call returns a promise which may be rejected by
browser autoplay policies if there was no user gesture. Wrap calls to
play() in a try/catch and provide a fallback UI so your app
handles playback interruptions gracefully.
\| `pause()` | Pause playback immediately. |
\| `seek(time)` | Seek when metadata is available. Ignored for live streams. |
Live streams: For live streams (when isLive is true) seeking is disabled and
long timeouts are used. Live streams are best handled with a separate
code-path because the playback semantics differ from on-demand audio files.
#### Volume
| Method | Description |
| ------------------------------ | --------------------------------------------------------------- |
| `setVolume(volume, fadeTime?)` | Set or fade volume (0–1). If `fadeTime` > 0, animates smoothly. |
| `getVolume()` | Return current volume (0–1). |
| `setMuted(muted)` | Mute or unmute. Restores previous volume when unmuting. |
#### State
| Method | Description |
| --------------------- | -------------------------------------------------------------- |
| `getDuration()` | Return loaded source duration (seconds) or `0` if unavailable. |
| `getCurrentTime()` | Return current playback position (seconds). |
| `isPaused()` | Return boolean — is playback paused. |
| `getBufferedRanges()` | Return underlying `TimeRanges` or `null`. |
| `getSource()` | Return current source URL string. |
| `getAudioElement()` | Return raw `HTMLAudioElement` or `null` on server. |
#### Events
The library emits custom events via an internal `EventTarget`:
```tsx
$audio.addEventListener("bufferingStart", () => console.log("Buffering..."))
$audio.addEventListener("bufferingEnd", () => console.log("Ready to play"))
$audio.addEventListener("playbackStarted", () => console.log("Playing"))
$audio.addEventListener("audioError", () => console.error("Error"))
$audio.addEventListener("bufferUpdate", (e) => {
if (e instanceof CustomEvent) console.log("Buffered:", e.detail.bufferedTime)
})
```
## Utilities
### formatDuration(seconds)
Format seconds to `MM:SS` string. Handles invalid input by returning `"0:00"`.
```tsx
import { formatDuration } from "@/lib/audio"
formatDuration(125) // "2:05"
formatDuration(3661) // "61:01"
```
### isLive(track)
Heuristic check for live streams based on URL patterns or `track.live` flag.
```tsx
import { isLive } from "@/lib/audio"
if (isLive(track)) {
// Handle live stream (no seeking)
}
```
## Types
### Track
Common audio track object with optional fields:
| Prop | Type | Default | Description |
| --------------- | ------------------ | ------- | -------------------------------- |
| `id` | `string \| number` | - | Unique identifier for the track. |
| `url` | `string` | - | URL of the audio file or stream. |
| `title` | `string` | - | Track title. |
| `artist` | `string` | - | Artist name. |
| `artwork` | `string` | - | Album artwork URL. |
| `images` | `string[]` | - | Array of image URLs. |
| `duration` | `number` | - | Track duration in seconds. |
| `album` | `string` | - | Album name. |
| `genre` | `string` | - | Genre. |
| `live` | `boolean` | - | Whether this is a live stream. |
| `[key: string]` | `unknown` | - | Additional properties. |
## Examples
### Basic Playback
```tsx
import { $audio } from "@/lib/audio"
$audio.init()
async function playTrack(url: string) {
try {
await $audio.load(url, 0)
await $audio.play()
} catch (error) {
console.error("Playback failed:", error)
}
}
```
### Volume Management
```tsx
import { $audio } from "@/lib/audio"
// Immediate change
$audio.setVolume(0.5)
// Smooth fade over 1 second
$audio.setVolume(0.8, 1000)
// Mute with memory
$audio.setMuted(true)
$audio.setMuted(false) // Restores previous volume
```
### React Component
```tsx
import { $audio, formatDuration } from "@/lib/audio"
import { useEffect, useState } from "react"
function TimeDisplay() {
const [time, setTime] = useState(0)
useEffect(() => {
const updateTime = () => setTime($audio.getCurrentTime())
const interval = setInterval(updateTime, 100)
return () => clearInterval(interval)
}, [])
return {formatDuration(time)}
}
```
### Event Monitoring
```tsx
import { $audio } from "@/lib/audio"
$audio.addEventListener("bufferingStart", () => {
console.log("Loading audio...")
})
$audio.addEventListener("bufferingEnd", () => {
console.log("Ready to play")
})
$audio.addEventListener("audioError", (event) => {
console.error("Audio error:", event)
})
```
## Related
* [Audio Store](/docs/lib/audio-store) — Zustand store for queue and playback state management
* [Audio Player](/docs/components/player) — Composable player UI components
## Notes
* Singleton pattern: All methods access the same `$audio` instance
* Server-safe: Methods check for client-side availability before executing
* Automatic retries: Handles load/play errors with exponential backoff (max 3 attempts)
* Volume fading: Animations use `requestAnimationFrame` for smooth transitions
* Live streams: Disable seeking and use extended timeout for reliability
* `formatDuration` handles edge cases (NaN, Infinity, negative values)
# Slider
## Installation
CLI
Manual
```bash
npx shadcn@latest add @audio/slider
```
Install the following dependencies:
```bash
npx install radix-ui
```
Copy and paste the following code into your project.
Update the import paths to match your project setup.
## Usage
```tsx
import { Slider } from "@/components/ui/slider"
```
```tsx
```
### Buffer Indicator
This slider extends the shadcn/ui slider component to add support for a `bufferValue` prop, which displays a buffer indicator behind the current value. This is particularly useful for media players to show loading progress:
```tsx showLineNumbers
import { Slider } from "@/components/ui/slider"
export function SliderWithBuffer() {
return (
)
}
```
## API Reference
### Slider
This component extends the shadcn/ui Slider component and inherits all its props. Additionally, it supports:
| Prop | Type | Default | Description |
| ------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| `bufferValue` | `number` | - | Buffer indicator value (0-100). Displays a semi-transparent overlay behind the current value. Useful for showing loading progress in media players. |
For all other props, see the [Radix UI Slider API Reference](https://www.radix-ui.com/docs/primitives/components/slider#api-reference).
## Notes
Important Information:
* **Extension:** This component extends the official shadcn/ui Slider component. All standard slider functionality is preserved, with the addition of the bufferValue prop for buffer indicators.
* **Buffer Indicator:** The buffer indicator displays a semi-transparent overlay behind the current value. This is particularly useful for media players to show loading progress or buffered content.
# Sortable List
## Installation
CLI
Manual
```bash
npx shadcn@latest add @audio/sortable-list
```
Install the following dependencies:
```bash
npm install @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities lucide-react
```
Install the required shadcn/ui primitives:
```bash
npx shadcn@latest add button
```
Copy and paste the following code into your project.
Update the import paths to match your project setup.
## Usage
```tsx
import {
SortableList,
SortableItem,
SortableDragHandle,
} from "@/components/ui/sortable-list"
```
```tsx showLineNumbers
"use client"
import { useState } from "react"
import {
Item,
ItemActions,
ItemContent,
ItemDescription,
ItemTitle,
} from "@/components/ui/item"
import {
SortableList,
SortableItem,
SortableDragHandle,
} from "@/components/ui/sortable-list"
const initialItems = [
{
id: "1",
title: "Item 1",
description: "Description for item 1",
},
{
id: "2",
title: "Item 2",
description: "Description for item 2",
},
{
id: "3",
title: "Item 3",
description: "Description for item 3",
},
]
export function SortableListDemo() {
const [items, setItems] = useState(initialItems)
return (
(
{item.title}{item.description}
)}
/>
)
}
```
## API Reference
### SortableList
The main container component for sortable items. Built with `@dnd-kit` for accessibility and smooth drag-and-drop interactions.
#### Props
| Prop | Type | Description |
| ------------ | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| `items` | `TItem[]` | Array of items to display. Each item must have an `id` property. |
| `onChange` | `(items: TItem[]) => void` | Callback fired when items are reordered. |
| `renderItem` | `(item: TItem, index: number, isOverlay?: boolean) => React.ReactNode` | Function to render each item. The `isOverlay` parameter indicates when an item is being dragged. |
| `className` | `string` | Additional CSS classes for the list container. |
### SortableItem
A wrapper component for individual sortable items.
#### Props
| Prop | Type | Description |
| ----------- | ------------------ | ---------------------------------------------------- |
| `id` | `string \| number` | Unique identifier for the item (must match item.id). |
| `className` | `string` | Additional CSS classes. |
| `children` | `React.ReactNode` | Content of the sortable item. |
### SortableDragHandle
A pre-built drag handle button that uses the sortable context. Extends shadcn/ui's `Button` component.
#### Props
Inherits all props from `Button` component.
## Notes
Important Information:
* **Built with dnd-kit:** This component is built on top of @dnd-kit, which provides full keyboard navigation support. Users can reorder items using arrow keys and keyboard shortcuts. See the dnd-kit documentation for more details.
* **Drag Handle:** The SortableDragHandle component uses shadcn/ui's Button component and provides a pre-configured drag handle with proper accessibility attributes.
* **Item IDs:** Each item must have a unique id property. The id type must match between the item object and the SortableItem component.