Vision UI

Router

A custom routing system that combines React Navigation patterns with SwiftUI's Ornament and Stack behaviors

Router

A custom routing system that combines React Navigation patterns with SwiftUI's Ornament and Stack behaviors, designed specifically for Vision OS applications.

Overview

The Vision UI Router provides a comprehensive navigation solution that merges the declarative approach of React Navigation with the spatial design principles of SwiftUI's Ornament and Stack navigators. It's built on top of Next.js catch-all routes and uses Radix UI primitives for accessibility and performance.

Key Features

  • React Navigation Patterns: Familiar API similar to React Navigation
  • SwiftUI Ornament Behavior: Floating navigation elements that maintain position
  • Stack Navigation: Hierarchical navigation with back button support
  • Vision OS Integration: Window controls and spatial design
  • TypeScript Support: Full type safety for route configurations
  • Next.js Integration: Works seamlessly with Next.js routing

Basic Setup

1. Router Provider

Wrap your application with the RouterProvider:

import { RouterProvider, NavigationContainer } from "@/components/core/v2/router";

export default function App() {
  return (
    <RouterProvider>
      <NavigationContainer>
        {/* Your navigation structure */}
      </NavigationContainer>
    </RouterProvider>
  );
}

2. Ornament Navigator (Tab-based)

The Ornament navigator creates floating tab bars similar to SwiftUI's Ornament:

import { Ornament } from "@/components/core/v2/router";

function MyApp() {
  return (
    <Ornament defaultRoute="home">
      <Ornament.Screen
        name="home"
        path="/home"
        component={HomeScreen}
        icon={<HomeIcon />}
        title="Home"
      />
      <Ornament.Screen
        name="photos"
        path="/photos"
        component={PhotosScreen}
        icon={<PhotosIcon />}
        title="Photos"
      />
      <Ornament.Screen
        name="settings"
        path="/settings"
        component={SettingsScreen}
        icon={<SettingsIcon />}
        title="Settings"
      />
    </Ornament>
  );
}

3. Stack Navigator

The Stack navigator provides hierarchical navigation with headers:

import { Stack } from "@/components/core/v2/router";

function PhotosStack() {
  return (
    <Stack defaultRoute="photos" headerShown={true}>
      <Stack.Screen
        name="photos"
        path="/photos"
        component={PhotosScreen}
        options={{
          title: "Photos",
          headerShown: true,
        }}
      />
      <Stack.Screen
        name="detail"
        path="/photos/detail"
        component={PhotoDetailScreen}
        options={{
          title: "Photo Details",
          headerShown: true,
        }}
      />
    </Stack>
  );
}

API Reference

RouterProvider

The root provider that manages navigation state.

type RouterProviderProps = {
  children: React.ReactNode;
  initialPath?: string;
};

Container for navigation content.

type NavigationContainerProps = {
  children: React.ReactNode;
  className?: string;
};

Ornament

Tab-based navigator with floating ornament behavior.

type OrnamentProps = {
  children: React.ReactNode;
  defaultRoute?: string;
  material?: boolean | { thickness?: GlassThickness };
  className?: string;
  contentClassName?: string;
  orientation?: "vertical" | "horizontal";
  position?: "left" | "right" | "top" | "bottom";
  floating?: boolean;
};

Ornament.Screen

Configuration for individual tab screens.

type OrnamentScreenProps = {
  name: string;
  path: string;
  component?: React.ComponentType<any>;
  icon?: React.ReactNode;
  title?: string;
  options?: {
    headerShown?: boolean;
    headerLeft?: React.ReactNode | (() => React.ReactNode);
    headerRight?: React.ReactNode | (() => React.ReactNode);
    className?: string;
  };
};

Stack

Hierarchical navigator with header support.

type StackProps = {
  children: React.ReactNode;
  defaultRoute?: string;
  material?: boolean | { thickness?: GlassThickness };
  className?: string;
  headerShown?: boolean;
  windowControls?: boolean;
};

Stack.Screen

Configuration for individual stack screens.

type StackScreenProps = {
  name: string;
  path: string;
  component?: React.ComponentType<any>;
  options?: {
    title?: string;
    headerShown?: boolean;
    headerLeft?: React.ReactNode | (() => React.ReactNode);
    headerRight?: React.ReactNode | (() => React.ReactNode);
    className?: string;
  };
};

useRouter

Access navigation functions and state:

import { useRouter } from "@/components/core/v2/router";

function MyComponent() {
  const { navigate, goBack, currentPath, history } = useRouter();

  return (
    <div>
      <button onClick={() => navigate("/photos")}>Go to Photos</button>
      <button onClick={goBack}>Go Back</button>
      <p>Current path: {currentPath}</p>
    </div>
  );
}

Advanced Usage

Nested Navigation

Combine Ornament and Stack navigators for complex navigation patterns:

function App() {
  return (
    <RouterProvider>
      <NavigationContainer>
        <Ornament defaultRoute="home">
          <Ornament.Screen
            name="home"
            path="/"
            component={HomeScreen}
            icon={<HomeIcon />}
            title="Home"
          />
          <Ornament.Screen
            name="photos"
            path="/photos"
            component={PhotosStack}
            icon={<PhotosIcon />}
            title="Photos"
          />
        </Ornament>
      </NavigationContainer>
    </RouterProvider>
  );
}

function PhotosStack() {
  return (
    <Stack defaultRoute="photos">
      <Stack.Screen
        name="photos"
        path="/photos"
        component={PhotosScreen}
        options={{ title: "Photos" }}
      />
      <Stack.Screen
        name="detail"
        path="/photos/detail"
        component={PhotoDetailScreen}
        options={{ title: "Photo Details" }}
      />
    </Stack>
  );
}

Custom Headers

Add custom header content:

<Stack.Screen
  name="detail"
  path="/photos/detail"
  component={PhotoDetailScreen}
  options={{
    title: "Photo Details",
    headerLeft: () => <CustomBackButton />,
    headerRight: () => <ShareButton />,
  }}
/>

Material Design

Customize the glass material appearance:

<Ornament
  material={{ thickness: "thick" }}
  orientation="vertical"
  position="left"
  floating={true}
>
  {/* Screens */}
</Ornament>

Next.js Integration

Catch-all Routes

Use Next.js catch-all routes for dynamic routing:

// app/[...slug]/page.tsx
export default function CatchAllPage() {
  const params = useParams();
  const route = Array.isArray(params.slug) ? params.slug.join("/") : params.slug;

  return (
    <RouterProvider>
      <NavigationContainer>
        {/* Route-based navigation logic */}
      </NavigationContainer>
    </RouterProvider>
  );
}

Dynamic Routes

Handle dynamic parameters:

// app/photos/[id]/page.tsx
export default function PhotoPage() {
  const params = useParams();
  
  return (
    <RouterProvider>
      <NavigationContainer>
        <Stack defaultRoute="detail">
          <Stack.Screen
            name="detail"
            path={`/photos/${params.id}`}
            component={PhotoDetailScreen}
            options={{ title: `Photo ${params.id}` }}
          />
        </Stack>
      </NavigationContainer>
    </RouterProvider>
  );
}

Vision OS Features

Window Controls

Automatic Vision OS-style window controls:

<Stack windowControls={true}>
  {/* Stack content */}
</Stack>

Spatial Design

The router is designed with spatial computing in mind:

  • Floating ornaments that maintain position
  • Smooth transitions between screens
  • Glass material effects
  • Proper z-index layering

Best Practices

  1. Route Organization: Use descriptive route names and organize them logically
  2. Component Separation: Keep screen components separate from navigation logic
  3. Type Safety: Use TypeScript for better development experience
  4. Accessibility: Ensure proper ARIA labels and keyboard navigation
  5. Performance: Lazy load components when possible

Examples

See the following examples for complete implementations:

Edit on GitHub

Last updated on