Router

Router API

Complete API reference for client-side routing and navigation.

Router API

The Manic Router provides a type-safe, zero-config routing system for client-side navigation with built-in code-splitting and View Transitions support.

Components

Primary component for client-side navigation. Automatically prefetches code chunks on hover.

// @errors: 2322
import React from 'react';
import { Link } from 'manicjs';

export default function Nav() {
  return (
    <Link to="/posts/123" prefetch={true}>
      View Post
    </Link>
  );
}

Props

Prop

Type

Example: Conditional Navigation

import React from 'react';
import {  } from 'manicjs';

export function ({ ,  }: { : string; ?: boolean }) {
  return (
    <
      ={`/blog/${}`}
      ={}
      ={ ? 'font-bold' : ''}
    >
      {}
    </>
  );
}

Hooks

useRouter()

Access the router instance for programmatic navigation.

// @errors: 2322
import React from 'react';
import { useRouter } from 'manicjs';

export function LoginButton() {
  const router = useRouter();

  const handleLogin = async () => {
    // ... login logic
    router.navigate('/dashboard');
  };

  return <button onClick={handleLogin}>Login</button>;
}

Router API

Prop

Type

Examples

// @errors: 2322
const router = useRouter();

// Push a new entry to history
router.navigate('/posts/456');

// Replace current history entry
router.navigate('/login', { replace: true });

// Check current path
if (router.path === '/') {
  // Home page
}

// Access route params
const postId = router.params.id;

Route Parameters

Access dynamic route parameters via useRouter().params:

// @errors: 2322
import React from 'react';
import { useRouter, useQueryParams } from 'manicjs';

export default function PostPage() {
  const { params } = useRouter();
  const slug = params.slug;
  
  return <div>Post: {slug}</div>;
}

Usage

import {  } from 'manicjs';

// Route: app/routes/blog/[slug].tsx
const {  } = ();
const {  } = ;  // From URL: /blog/my-post

// Route: app/routes/posts/[id]/comments/[commentId].tsx
const { ,  } = ;

// Route: app/routes/docs/[...path].tsx (catch-all)
const {  } = ;  // path = "guides/setup/installation"

useQueryParams()

Access and update URL search parameters:

import React from 'react';
import {  } from 'manicjs';
declare const : any;
declare const : (: string) => void;

export function () {
  const  = ();

  return (
    <>
      <
        ={.('q') || ''}
        ={(: any) => {
          const  = new ();
          .('q', .target.value);
          (`?${.()}`);
        }}
      />
      <>Searching for: {.('q')}</>
    </>
  );
}

API

useQueryParams() returns a standard URLSearchParams object:

class URLSearchParams {
  get(key: string): string | null;
  getAll(key: string): string[];
  has(key: string): boolean;
  set(key: string, value: string): void;
  delete(key: string): void;
  toString(): string;
}

Examples

import {  } from 'manicjs';

declare function (: string): void;

const  = ();

// Read
const  = .('q');           // "react"
const  = .('page');             // "2"
const  = .('tag');           // ["javascript", "react"]

// Check existence
if (.('sort')) {
  // Sort param exists
}

// Update URL manually
const  = new ();
.('q', 'typescript');
.('page');
(`?${.()}`);

Route Manifest

The router relies on an auto-generated manifest at app/~routes.generated.ts. This file is updated automatically by manic dev and manic build.

Never edit app/~routes.generated.ts manually. It is regenerated on every build.

Example Generated Manifest

// app/~routes.generated.ts (auto-generated)
export const routes = [
  {
    path: '/',
    component: null,
    loader: () => import('./routes/index.tsx'),
  },
  {
    path: '/blog/:slug',
    component: null,
    loader: () => import('./routes/blog/[slug].tsx'),
  },
  {
    path: '/docs/:...path',
    component: null,
    loader: () => import('./routes/docs/[...path].tsx'),
  },
];

Route Registry Structure

Initializing diagram...

Architecture

Initializing diagram...

Advanced Patterns

Protected Routes

// @errors: 2322
import React from 'react';
import { useRouter, useQueryParams } from 'manicjs';
import { useAuth } from './~hooks/useAuth';

export default function AdminPage() {
  const router = useRouter();
  const { user } = useAuth();

  React.useEffect(() => {
    if (!user?.isAdmin) {
      router.navigate('/', { replace: true });
    }
  }, [user, router]);

  if (!user?.isAdmin) return null;  // Prevent flash
  
  return <div>Admin Panel</div>;
}

Search with Query Params

import React from 'react';
import { ,  } from 'manicjs';

declare function (: string): void;

export default function () {
  const  = ();
  const  = ();
  const  = .('q') || '';

  const  = (: string) => {
    const  = new ();
    .('q', );
    .('page', '1');
    (`?${.()}`);
  };

  return (
    <>
      < ={} ={(: any) => (.target.value)} />
      {/* Results for {searchTerm} */}
    </>
  );
}

Nested Dynamic Routes

// Route: app/routes/posts/[id]/comments/[commentId].tsx
import React from 'react';
import {  } from 'manicjs';

export default function () {
  const {  } = ();
  
  return (
    <>
      <>Post {.}</>
      <>Comment {.}</>
    </>
  );
}

View Transitions

Manic automatically wraps navigation in document.startViewTransition() for smooth page transitions.

import React from 'react';
import {  } from 'manicjs';

export default function () {
  return (
    < ="/about">
      About
    </>
  );
}

Disable transitions globally:

import {  } from 'manicjs';

(false);

Custom transition CSS:

::view-transition-old(root) {
  animation: fadeOut 0.3s ease-out;
}

::view-transition-new(root) {
  animation: fadeIn 0.3s ease-in;
}

Router hooks must be called inside components rendered by the router. Calling them outside a routed component will error.

See routing guide for more patterns and best practices.

On this page