# How Resource-Based Routes Work in refine-shadcn: From Declaration to Navigation

> Understand how resource-based routes work in refine-shadcn. Connect resource declarations to dynamic URLs for automatic navigation, permission-aware routing, and reusable CRUD components in React.

- Repository: [Ferdi ÜNAL/refine-shadcn](https://github.com/ferdiunal/refine-shadcn)
- Tags: deep-dive
- Published: 2026-03-01

---

**Resource-based routes in refine-shadcn connect declarative resource definitions to dynamic URLs through a centralized `resources` array, enabling automatic navigation generation, permission-aware routing, and reusable CRUD components across your React application.**

The `ferdiunal/refine-shadcn` library implements a declarative routing system where **resource-based routes** drive the entire UI navigation. Instead of hardcoding paths throughout your application, you define entities once in a static configuration file, and the framework automatically generates URLs, sidebar links, and access-controlled actions. This architecture allows you to add new entities like "comments" or "users" by simply updating a single array, with all navigation components adapting instantly.

## Declaring Resources in src/resources.tsx

All resource-based routes originate from a single source of truth. In [`templates/vite-react/src/resources.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/templates/vite-react/src/resources.tsx), you export an array of `ResourceProps` objects that declare each entity's name, base path, and available CRUD operations.

```tsx
// templates/vite-react/src/resources.tsx
export const resources: ResourceProps[] = [
    {
        name: "dashboard",
        list: "/dashboard",
        meta: { title: "Dashboard", icon: <Home .../> },
    },
    {
        name: "posts",
        list: "/posts",
        show: "/posts/show/:id",
        create: "/posts/create",
        edit: "/posts/edit/:id",
        meta: { title: "Posts", icon: <NewspaperIcon .../> },
    },
];

```

Each resource object specifies the **list**, **show**, **create**, and **edit** route patterns. The `meta` field stores UI-specific data like titles and icons. When you add a new resource here, the entire application instantly recognizes the new routes without requiring changes to navigation components or CRUD pages.

## Accessing the Resource Context with useResource

The `@refinedev/core` package automatically registers your resources array in a global **ResourceProvider**. Components throughout the application access this data via the `useResource` hook, as implemented in [`packages/theme/src/layouts/default.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/layouts/default.tsx).

```tsx
// packages/theme/src/layouts/default.tsx
import { useResource } from "@refinedev/core";

const DefaultLayout = () => {
    const { resources } = useResource();
    const firstDashboard = resources?.[0]; // Used for logo link
    
    return (
        // Layout renders Sidebar with full resources list
    );
};

```

This hook returns the complete `resources` array along with helper methods to retrieve specific entries by name. By consuming this context, the `DefaultLayout` dynamically sets the logo link to the first resource (typically "dashboard") and passes the resource configuration down to child components.

## Generating URLs with useGetShowUrl and useGetEditUrl

Concrete URL generation happens through specialized hooks that combine routing logic with access control. The `useGetShowUrl` hook in [`packages/theme/src/hooks/useGetShowUrl.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/hooks/useGetShowUrl.tsx) demonstrates this pattern.

```tsx
// packages/theme/src/hooks/useGetShowUrl.tsx
const { showUrl: generateShowUrl } = useNavigation();
const { id, resource: _resource } = useResource(resource);
const { data } = useCan({ 
    resource, 
    action: "show", 
    params: { id: recordItemId, resource: _resource } 
});

const showUrl = resource && (recordItemId || id) 
    ? generateShowUrl(resource, recordItemId ?? id, meta) 
    : "";

```

This hook performs two critical functions. First, it calls `useNavigation()` to generate the base path while substituting the `:id` placeholder with the actual record identifier. Second, it invokes `useCan` to verify the user has permission for the "show" action, returning a `can` boolean and localized `reason` if access is denied. The companion `useGetEditUrl` hook in [`packages/theme/src/hooks/useGetEditUrl.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/hooks/useGetEditUrl.tsx) implements identical logic for edit routes.

## Building Dynamic Navigation in Sidebar.tsx

The sidebar component in [`packages/theme/src/components/sidebar.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/components/sidebar.tsx) iterates over the resource definitions to construct navigation links automatically. It generates possible route patterns for each resource to determine active states.

```tsx
// packages/theme/src/components/sidebar.tsx
const paths = [
    item.list?.toString(),
    item.create?.toString(),
    item.edit?.toString()?.replace(":id", resourceParams.id as string),
    item.show?.toString()?.replace(":id", resourceParams.id as string),
].filter(Boolean);

```

For each menu item derived from the resources array, the component builds an array of potential paths by resolving dynamic segments like `:id` against current URL parameters. When the current pathname matches any entry in this array, the sidebar marks that navigation item as active. The component also handles collapsed states, wrapping items in tooltips when `isCollapsed` is true.

## Implementing Resource-Agnostic CRUD Actions

Table components leverage resource-based routes to render action buttons without hardcoding URLs. In [`templates/vite-react/src/pages/posts/list.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/templates/vite-react/src/pages/posts/list.tsx), the `Table` component receives a `resource` prop that drives all navigation.

```tsx
// templates/vite-react/src/pages/posts/list.tsx
<Table.ShowAction
    title="Detail"
    row={original}
    resource="posts"
    icon={<Eye size={16} />}
/>
<Table.EditAction
    title="Edit"
    row={original}
    resource="posts"
    icon={<Edit size={16} />}
/>

```

Because these actions receive the resource name "posts", they internally call `useGetShowUrl` and `useGetEditUrl` to resolve `/posts/show/:id` and `/posts/edit/:id` respectively. This design makes the `Table` component completely reusable—simply change the `resource` prop to "comments" or "users", and all URLs update automatically while maintaining proper access control checks.

## Adding a New Resource Without Code Changes

The true power of resource-based routes emerges when extending your application. To add a "comments" entity, you only modify [`templates/vite-react/src/resources.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/templates/vite-react/src/resources.tsx):

```tsx
{
    name: "comments",
    list: "/comments",
    show: "/comments/show/:id",
    create: "/comments/create",
    edit: "/comments/edit/:id",
    meta: { title: "Comments", icon: <MessageSquare .../> },
}

```

Upon saving, the sidebar instantly displays a "Comments" navigation item, the layout includes it in breadcrumb calculations (handled in [`packages/theme/src/components/breadcrumbs.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/components/breadcrumbs.tsx)), and all CRUD tables can reference `resource="comments"` without touching any routing logic.

## Summary

- **Centralized configuration**: Define all routes in [`templates/vite-react/src/resources.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/templates/vite-react/src/resources.tsx) using the `ResourceProps` interface.
- **Global access**: Retrieve resource data anywhere via `useResource()` from `@refinedev/core`.
- **Dynamic URL generation**: Use `useGetShowUrl` and `useGetEditUrl` to convert resource names and record IDs into accessible URLs with built-in permission checking.
- **Automatic navigation**: The `Sidebar` component in [`packages/theme/src/components/sidebar.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/components/sidebar.tsx) builds links and active states directly from the resources array.
- **Reusable CRUD**: Table actions accept a `resource` prop to generate correct routes for any entity without hardcoded paths.

## Frequently Asked Questions

### How do I add access control to resource-based routes?

The `useGetShowUrl` and `useGetEditUrl` hooks automatically integrate with `useCan` from `@refinedev/core` to check permissions before generating URLs. When you call these hooks with a resource name and record ID, they return a `can` boolean indicating whether the user has rights to perform the action, along with a localized `reason` string if access is denied. Implement these hooks in your components to conditionally render action buttons or redirect unauthorized users.

### Can I use resource-based routes outside of the standard CRUD pages?

Yes. Any component can import `useGetEditUrl` from `@ferdiunal/refine-shadcn` to generate URLs for custom implementations. For example, in a custom edit page, you can resolve the edit URL for a "comments" resource by calling the hook with the resource name and ID, then use the returned URL in a form action or router link while respecting the permission system.

### Where does refine-shadcn store the active resource list?

The framework stores resources in a **ResourceProvider** managed by `@refinedev/core`, which reads your `resources` array from [`templates/vite-react/src/resources.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/templates/vite-react/src/resources.tsx) when the application initializes. Components like `DefaultLayout` in [`packages/theme/src/layouts/default.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/layouts/default.tsx) access this context via `useResource()` to render navigation and layout elements dynamically.

### How does the sidebar determine which navigation item is active?

The `Sidebar` component in [`packages/theme/src/components/sidebar.tsx`](https://github.com/ferdiunal/refine-shadcn/blob/main/packages/theme/src/components/sidebar.tsx) generates an array of possible route patterns for each resource—including list, create, edit, and show paths—by substituting `:id` placeholders with current URL parameters. It compares these generated paths against the current browser location using pattern matching, marking the corresponding menu item as active when a match occurs.