diff --git a/apps/www/src/components/playground/index.ts b/apps/www/src/components/playground/index.ts
index 62ec07833..5d32811a0 100644
--- a/apps/www/src/components/playground/index.ts
+++ b/apps/www/src/components/playground/index.ts
@@ -44,4 +44,5 @@ export * from './tabs-examples';
export * from './text-area-examples';
export * from './text-examples';
export * from './toast-examples';
+export * from './toolbar-examples';
export * from './tooltip-examples';
diff --git a/apps/www/src/components/playground/toolbar-examples.tsx b/apps/www/src/components/playground/toolbar-examples.tsx
new file mode 100644
index 000000000..bbe0eb77a
--- /dev/null
+++ b/apps/www/src/components/playground/toolbar-examples.tsx
@@ -0,0 +1,95 @@
+'use client';
+
+import {
+ FontBoldIcon,
+ FontItalicIcon,
+ TextAlignCenterIcon,
+ TextAlignLeftIcon,
+ TextAlignRightIcon,
+ UnderlineIcon
+} from '@radix-ui/react-icons';
+import { Button, Flex, IconButton, Text, Toolbar } from '@raystack/apsara';
+import PlaygroundLayout from './playground-layout';
+
+export function ToolbarExamples() {
+ return (
+
+
+ Default:
+
+ Bold
+ Italic
+
+ Left
+ Center
+ Right
+
+
+ Grouped with composition:
+
+ }
+ >
+ 32px
+
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+ }
+ >
+
+
+
+
+
+ Disabled:
+
+ Bold
+ Italic
+
+ Left
+
+
+
+ );
+}
diff --git a/apps/www/src/content/docs/components/toolbar/demo.ts b/apps/www/src/content/docs/components/toolbar/demo.ts
new file mode 100644
index 000000000..86db53cba
--- /dev/null
+++ b/apps/www/src/content/docs/components/toolbar/demo.ts
@@ -0,0 +1,61 @@
+'use client';
+
+export const preview = {
+ type: 'code',
+ code: `
+ Bold
+ Italic
+ Underline
+
+ Left
+ Center
+ Right
+`
+};
+
+export const groupDemo = {
+ type: 'code',
+ code: `
+
+ Bold
+ Italic
+ Underline
+
+
+
+ Left
+ Center
+ Right
+
+`
+};
+
+export const compositionDemo = {
+ type: 'code',
+ code: `
+ }>
+ 32px
+
+
+
+ }>
+
+
+ }>
+
+
+
+
+ Help
+`
+};
+
+export const disabledDemo = {
+ type: 'code',
+ code: `
+ Bold
+ Italic
+
+ Left
+`
+};
diff --git a/apps/www/src/content/docs/components/toolbar/index.mdx b/apps/www/src/content/docs/components/toolbar/index.mdx
new file mode 100644
index 000000000..256b86207
--- /dev/null
+++ b/apps/www/src/content/docs/components/toolbar/index.mdx
@@ -0,0 +1,95 @@
+---
+title: Toolbar
+description: A container for grouping interactive controls with accessible keyboard navigation.
+source: packages/raystack/components/toolbar
+tag: new
+---
+
+import { preview, groupDemo, compositionDemo, disabledDemo } from "./demo.ts";
+
+
+
+## Anatomy
+
+Import and assemble the component:
+
+```tsx
+import { Toolbar } from '@raystack/apsara'
+
+
+
+
+
+
+
+
+
+
+
+```
+
+## API Reference
+
+### Root
+
+Groups all toolbar controls. Manages keyboard navigation between items.
+
+
+
+### Button
+
+An interactive button within the toolbar. Supports the `render` prop to compose with other components like `Button` or `IconButton`.
+
+
+
+### Group
+
+Groups related toolbar items together.
+
+
+
+### Separator
+
+A visual divider between toolbar sections.
+
+
+
+### Link
+
+An anchor element for navigation within the toolbar.
+
+
+
+### Input
+
+A native input element with integrated keyboard navigation.
+
+
+
+## Examples
+
+### Grouped
+
+Use `Toolbar.Group` to group related buttons.
+
+
+
+### Composition
+
+Use the `render` prop on `Toolbar.Button` to compose with Apsara components like `Button` and `IconButton`.
+
+
+
+### Disabled
+
+Disable all toolbar items by setting `disabled` on the root.
+
+
+
+## Accessibility
+
+- Uses `role="toolbar"` for proper semantic grouping
+- Arrow keys navigate between focusable items
+- `Home` and `End` keys jump to first and last items
+- Focus loops by default (configurable via `loopFocus`)
+- Disabled items remain focusable for screen reader discoverability
diff --git a/apps/www/src/content/docs/components/toolbar/props.ts b/apps/www/src/content/docs/components/toolbar/props.ts
new file mode 100644
index 000000000..744642267
--- /dev/null
+++ b/apps/www/src/content/docs/components/toolbar/props.ts
@@ -0,0 +1,95 @@
+export interface ToolbarProps {
+ /**
+ * Whether keyboard navigation loops from the last item back to the first.
+ * @defaultValue true
+ */
+ loopFocus?: boolean;
+
+ /**
+ * The orientation of the toolbar layout.
+ * @defaultValue "horizontal"
+ */
+ orientation?: 'horizontal' | 'vertical';
+
+ /**
+ * Whether the toolbar and all its items are disabled.
+ * @defaultValue false
+ */
+ disabled?: boolean;
+
+ /** Additional CSS class names. */
+ className?: string;
+}
+
+export interface ToolbarButtonProps {
+ /**
+ * Whether the button remains focusable when disabled.
+ * @defaultValue true
+ */
+ focusableWhenDisabled?: boolean;
+
+ /**
+ * Whether the button is disabled.
+ * @defaultValue false
+ */
+ disabled?: boolean;
+
+ /**
+ * Allows you to replace the component's HTML element with a different tag, or compose it with another component.
+ *
+ * @remarks `ReactElement | function`
+ */
+ render?:
+ | React.ReactElement
+ | ((props: React.HTMLAttributes) => React.ReactElement);
+
+ /** Additional CSS class names. */
+ className?: string;
+}
+
+export interface ToolbarGroupProps {
+ /**
+ * Whether the group and all its items are disabled.
+ * @defaultValue false
+ */
+ disabled?: boolean;
+
+ /** Additional CSS class names. */
+ className?: string;
+}
+
+export interface ToolbarSeparatorProps {
+ /** Additional CSS class names. */
+ className?: string;
+}
+
+export interface ToolbarLinkProps {
+ /**
+ * Allows you to replace the component's HTML element with a different tag, or compose it with another component.
+ *
+ * @remarks `ReactElement | function`
+ */
+ render?:
+ | React.ReactElement
+ | ((props: React.HTMLAttributes) => React.ReactElement);
+
+ /** Additional CSS class names. */
+ className?: string;
+}
+
+export interface ToolbarInputProps {
+ /**
+ * Whether the input remains focusable when disabled.
+ * @defaultValue true
+ */
+ focusableWhenDisabled?: boolean;
+
+ /**
+ * Whether the input is disabled.
+ * @defaultValue false
+ */
+ disabled?: boolean;
+
+ /** Additional CSS class names. */
+ className?: string;
+}
diff --git a/packages/raystack/components/toolbar/__tests__/toolbar.test.tsx b/packages/raystack/components/toolbar/__tests__/toolbar.test.tsx
new file mode 100644
index 000000000..67b48e807
--- /dev/null
+++ b/packages/raystack/components/toolbar/__tests__/toolbar.test.tsx
@@ -0,0 +1,196 @@
+import { fireEvent, render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { createRef } from 'react';
+import { describe, expect, it, vi } from 'vitest';
+import { Toolbar } from '../toolbar';
+import styles from '../toolbar.module.css';
+
+describe('Toolbar', () => {
+ it('renders with children', () => {
+ render(
+
+ Bold
+
+ );
+ expect(screen.getByRole('toolbar')).toBeInTheDocument();
+ expect(screen.getByText('Bold')).toBeInTheDocument();
+ });
+
+ it('applies custom className to root', () => {
+ render(content);
+ const toolbar = screen.getByRole('toolbar');
+ expect(toolbar.className).toContain(styles.root);
+ expect(toolbar.className).toContain('custom');
+ });
+
+ it('forwards ref to root', () => {
+ const ref = createRef();
+ render(content);
+ expect(ref.current).toBeInstanceOf(HTMLDivElement);
+ expect(ref.current).toBe(screen.getByRole('toolbar'));
+ });
+
+ describe('Button', () => {
+ it('renders as a button', () => {
+ render(
+
+ Click
+
+ );
+ expect(screen.getByRole('button', { name: 'Click' })).toBeInTheDocument();
+ });
+
+ it('handles click events', async () => {
+ const onClick = vi.fn();
+ render(
+
+ Click
+
+ );
+ fireEvent.click(screen.getByRole('button'));
+ expect(onClick).toHaveBeenCalledTimes(1);
+ });
+
+ it('applies custom className', () => {
+ render(
+
+ Click
+
+ );
+ const button = screen.getByRole('button');
+ expect(button.className).toContain(styles.button);
+ expect(button.className).toContain('custom');
+ });
+
+ it('forwards ref', () => {
+ const ref = createRef();
+ render(
+
+ Click
+
+ );
+ expect(ref.current).toBeInstanceOf(HTMLButtonElement);
+ });
+ });
+
+ describe('Group', () => {
+ it('renders children in a group', () => {
+ render(
+
+
+ A
+ B
+
+
+ );
+ const group = screen.getByTestId('group');
+ expect(group.className).toContain(styles.group);
+ expect(screen.getByText('A')).toBeInTheDocument();
+ expect(screen.getByText('B')).toBeInTheDocument();
+ });
+
+ it('forwards ref', () => {
+ const ref = createRef();
+ render(
+
+
+ A
+
+
+ );
+ expect(ref.current).toBeInstanceOf(HTMLDivElement);
+ });
+ });
+
+ describe('Separator', () => {
+ it('renders a separator', () => {
+ render(
+
+
+
+ );
+ const sep = screen.getByTestId('sep');
+ expect(sep).toBeInTheDocument();
+ expect(sep.className).toContain(styles.separator);
+ });
+
+ it('forwards ref', () => {
+ const ref = createRef();
+ render(
+
+
+
+ );
+ expect(ref.current).toBeInstanceOf(HTMLDivElement);
+ });
+ });
+
+ describe('Link', () => {
+ it('renders a link', () => {
+ render(
+
+ Example
+
+ );
+ const link = screen.getByRole('link', { name: 'Example' });
+ expect(link).toBeInTheDocument();
+ expect(link).toHaveAttribute('href', 'https://example.com');
+ });
+
+ it('forwards ref', () => {
+ const ref = createRef();
+ render(
+
+
+ Link
+
+
+ );
+ expect(ref.current).toBeInstanceOf(HTMLAnchorElement);
+ });
+ });
+
+ describe('Input', () => {
+ it('renders an input', () => {
+ render(
+
+
+
+ );
+ expect(screen.getByTestId('input')).toBeInTheDocument();
+ });
+
+ it('forwards ref', () => {
+ const ref = createRef();
+ render(
+
+
+
+ );
+ expect(ref.current).toBeInstanceOf(HTMLInputElement);
+ });
+ });
+
+ describe('Keyboard navigation', () => {
+ it('navigates between buttons with arrow keys', async () => {
+ const user = userEvent.setup();
+ render(
+
+ First
+ Second
+ Third
+
+ );
+
+ const first = screen.getByText('First');
+ first.focus();
+ expect(first).toHaveFocus();
+
+ await user.keyboard('{ArrowRight}');
+ expect(screen.getByText('Second')).toHaveFocus();
+
+ await user.keyboard('{ArrowRight}');
+ expect(screen.getByText('Third')).toHaveFocus();
+ });
+ });
+});
diff --git a/packages/raystack/components/toolbar/index.tsx b/packages/raystack/components/toolbar/index.tsx
new file mode 100644
index 000000000..dc5abb129
--- /dev/null
+++ b/packages/raystack/components/toolbar/index.tsx
@@ -0,0 +1 @@
+export { Toolbar } from './toolbar';
diff --git a/packages/raystack/components/toolbar/toolbar.module.css b/packages/raystack/components/toolbar/toolbar.module.css
new file mode 100644
index 000000000..f9c52d459
--- /dev/null
+++ b/packages/raystack/components/toolbar/toolbar.module.css
@@ -0,0 +1,50 @@
+.root {
+ display: flex;
+ align-items: center;
+ gap: var(--rs-space-1);
+ padding: var(--rs-space-1);
+ border: 0.5px solid var(--rs-color-border-base-primary);
+ border-radius: var(--rs-radius-2);
+ overflow: clip;
+}
+
+.button {
+ cursor: pointer;
+ background: none;
+ border: none;
+ outline: none;
+ padding: var(--rs-space-1) var(--rs-space-2);
+ border-radius: var(--rs-radius-2);
+ font-family: var(--rs-font-body);
+ font-size: var(--rs-font-size-mini);
+ font-weight: var(--rs-font-weight-medium);
+ line-height: var(--rs-line-height-mini);
+ letter-spacing: var(--rs-letter-spacing-mini);
+ color: var(--rs-color-foreground-base-primary);
+}
+
+.button:hover,
+.button:focus-visible {
+ background-color: var(--rs-color-background-base-primary-hover);
+}
+
+.button[data-disabled] {
+ pointer-events: none;
+ opacity: 0.5;
+}
+
+.group {
+ display: flex;
+ align-items: center;
+}
+
+.group > * {
+ border: none;
+}
+
+.separator {
+ flex-shrink: 0;
+ width: 0.5px;
+ height: 16px;
+ background-color: var(--rs-color-border-base-primary);
+}
diff --git a/packages/raystack/components/toolbar/toolbar.tsx b/packages/raystack/components/toolbar/toolbar.tsx
new file mode 100644
index 000000000..54be0e3fd
--- /dev/null
+++ b/packages/raystack/components/toolbar/toolbar.tsx
@@ -0,0 +1,79 @@
+'use client';
+
+import { Toolbar as ToolbarPrimitive } from '@base-ui/react/toolbar';
+import { cx } from 'class-variance-authority';
+import { ElementRef, forwardRef } from 'react';
+
+import styles from './toolbar.module.css';
+
+const ToolbarRoot = forwardRef<
+ ElementRef,
+ ToolbarPrimitive.Root.Props
+>(({ className, ...props }, ref) => (
+
+));
+ToolbarRoot.displayName = 'Toolbar';
+
+const ToolbarButton = forwardRef<
+ ElementRef,
+ ToolbarPrimitive.Button.Props
+>(({ className, ...props }, ref) => (
+
+));
+ToolbarButton.displayName = 'Toolbar.Button';
+
+const ToolbarGroup = forwardRef<
+ ElementRef,
+ ToolbarPrimitive.Group.Props
+>(({ className, ...props }, ref) => (
+
+));
+ToolbarGroup.displayName = 'Toolbar.Group';
+
+const ToolbarSeparator = forwardRef<
+ ElementRef,
+ ToolbarPrimitive.Separator.Props
+>(({ className, ...props }, ref) => (
+
+));
+ToolbarSeparator.displayName = 'Toolbar.Separator';
+
+const ToolbarLink = forwardRef<
+ ElementRef,
+ ToolbarPrimitive.Link.Props
+>(({ className, ...props }, ref) => (
+
+));
+ToolbarLink.displayName = 'Toolbar.Link';
+
+const ToolbarInput = forwardRef<
+ ElementRef,
+ ToolbarPrimitive.Input.Props
+>(({ className, ...props }, ref) => (
+
+));
+ToolbarInput.displayName = 'Toolbar.Input';
+
+export const Toolbar = Object.assign(ToolbarRoot, {
+ Button: ToolbarButton,
+ Group: ToolbarGroup,
+ Separator: ToolbarSeparator,
+ Link: ToolbarLink,
+ Input: ToolbarInput
+});
diff --git a/packages/raystack/index.tsx b/packages/raystack/index.tsx
index 37885b603..edf337c04 100644
--- a/packages/raystack/index.tsx
+++ b/packages/raystack/index.tsx
@@ -68,4 +68,5 @@ export {
useTheme
} from './components/theme-provider';
export { Toast, toastManager } from './components/toast';
+export { Toolbar } from './components/toolbar';
export { Tooltip } from './components/tooltip';