Skip to main content
useParams is a React hook that returns an object containing the dynamic parameters from the current URL. It’s re-exported from react-router v7 and is essential for building dynamic routes in AppShell applications.
Always import useParams from @tailor-platform/app-shell, not from react-router directly. AppShell manages its own router instance, and importing from the wrong package will cause the hook to return empty values.

Usage

import { useParams } from '@tailor-platform/app-shell';

const ProductDetail = () => {
  const { id } = useParams();

  return <h1>Product ID: {id}</h1>;
};

Signature

const params: Params<string> = useParams();

// With TypeScript type parameter
const params = useParams<{ id: string }>();

type Params<Key extends string = string> = {
  readonly [key in Key]: string | undefined;
};

Examples

Single Parameter

For a route defined as /orders/:id/page.tsx:
import { useParams } from '@tailor-platform/app-shell';

const OrderDetail = () => {
  const { id } = useParams<{ id: string }>();

  return (
    <div>
      <h1>Order #{id}</h1>
      <p>View details for order {id}</p>
    </div>
  );
};

Multiple Parameters

For a route like /orders/:orderId/items/:itemId/page.tsx:
import { useParams } from '@tailor-platform/app-shell';

const OrderItemDetail = () => {
  const { orderId, itemId } = useParams<{
    orderId: string;
    itemId: string;
  }>();

  return (
    <div>
      <h1>Order {orderId}</h1>
      <h2>Item {itemId}</h2>
    </div>
  );
};

Using Params in Data Fetching

import { useParams } from '@tailor-platform/app-shell';
import { useQuery } from '@apollo/client';

const GET_PRODUCT = gql`
  query GetProduct($id: ID!) {
    product(id: $id) {
      id
      name
      price
    }
  }
`;

const ProductPage = () => {
  const { id } = useParams<{ id: string }>();
  const { data, loading, error } = useQuery(GET_PRODUCT, {
    variables: { id },
  });

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;

  return (
    <div>
      <h1>{data.product.name}</h1>
      <p>Price: ${data.product.price}</p>
    </div>
  );
};
Combine useParams with useNavigate for dynamic navigation:
import { useParams, useNavigate } from '@tailor-platform/app-shell';

const OrderDetail = () => {
  const { id } = useParams<{ id: string }>();
  const navigate = useNavigate();

  const goToEdit = () => {
    navigate(`/orders/${id}/edit`);
  };

  const goToItems = () => {
    navigate(`/orders/${id}/items`);
  };

  return (
    <div>
      <h1>Order {id}</h1>
      <button onClick={goToEdit}>Edit Order</button>
      <button onClick={goToItems}>View Items</button>
    </div>
  );
};

Dynamic Breadcrumbs with Params

You can use params to create dynamic breadcrumb titles in your resource definitions:
import { defineResource, useParams } from '@tailor-platform/app-shell';

const orderDetailResource = defineResource({
  path: ':id',
  meta: {
    title: 'Order Details',
    // Function receives the param value
    breadcrumbTitle: (id) => `Order ${id}`,
  },
  component: () => {
    const { id } = useParams<{ id: string }>();
    return <div>Order {id} details...</div>;
  },
});

File-Based Routing

With AppShell’s file-based routing (Vite plugin), dynamic segments are defined using square brackets:
src/pages/
├── dashboard/
│   ├── orders/
│   │   ├── [id]/
│   │   │   └── page.tsx       # Route: /dashboard/orders/:id
│   │   └── page.tsx           # Route: /dashboard/orders
// src/pages/dashboard/orders/[id]/page.tsx
import { useParams } from '@tailor-platform/app-shell';

const OrderDetailPage = () => {
  const { id } = useParams<{ id: string }>();
  
  return <h1>Order {id}</h1>;
};

export default OrderDetailPage;

Module-Based Routing

With module definitions, use :paramName syntax:
import { defineResource, useParams } from '@tailor-platform/app-shell';

const orderDetailResource = defineResource({
  path: ':id',  // Creates /orders/:id route
  component: () => {
    const { id } = useParams<{ id: string }>();
    return <div>Order {id}</div>;
  },
});

Type Safety

With TypeScript

Always provide type parameters for better type safety:
// ✅ Good - type-safe params
const { id } = useParams<{ id: string }>();

// ❌ Avoid - params are typed as string | undefined
const params = useParams();
const id = params.id; // string | undefined

Optional Parameters

All parameters are technically optional (can be undefined):
const { id } = useParams<{ id?: string }>();

if (!id) {
  return <div>No ID provided</div>;
}

// Use id safely here
return <div>Order {id}</div>;

Common Patterns

Guard Against Missing Params

const ProductDetail = () => {
  const { id } = useParams<{ id: string }>();

  if (!id) {
    return <div>Product ID is required</div>;
  }

  return <div>Product {id}</div>;
};

Parse Numeric IDs

const OrderDetail = () => {
  const { id } = useParams<{ id: string }>();
  const orderId = id ? parseInt(id, 10) : null;

  if (!orderId || isNaN(orderId)) {
    return <div>Invalid order ID</div>;
  }

  return <div>Order #{orderId}</div>;
};

Combine with Other Route Data

import { useParams, useLocation } from '@tailor-platform/app-shell';

const OrderDetail = () => {
  const { id } = useParams<{ id: string }>();
  const location = useLocation();
  
  // Access query params
  const searchParams = new URLSearchParams(location.search);
  const tab = searchParams.get('tab') || 'details';

  return (
    <div>
      <h1>Order {id}</h1>
      <p>Current tab: {tab}</p>
    </div>
  );
};

See Also

Build docs developers (and LLMs) love