Skip to main content
Defines a resource that can be nested within modules or other resources. Resources represent individual pages or features in your application.

Signature

function defineResource(props: DefineResourceProps): Resource

Parameters

path
string
required
URL path for the resource. Supports dynamic segments using colon prefix (e.g., :id, :productSlug).
component
(props: ResourceComponentProps) => ReactNode
required
Component to render when this resource is active.Receives ResourceComponentProps:
  • title: string - Title of the resource
  • icon?: ReactNode - Optional icon
  • resources?: Resource[] - Optional sub-resources
meta
object
Metadata configuration for the resource.
meta.title
string | LocalizedString
Display title used in navigation and breadcrumbs. Defaults to path in capital case.
meta.icon
ReactNode
Icon displayed in navigation.
meta.breadcrumbTitle
string | ((segment: string) => string)
Custom breadcrumb segment title. Can be a static string or a function that takes the path segment and returns a title.
subResources
Resource[]
Nested resources under this resource. Define using defineResource().
guards
Guard[]
Array of guard functions to control access. Guards are executed in order. If any guard returns non-pass result, access is denied.See Route Guards for details.
errorBoundary
ReactNode
Error boundary component for this resource and its sub-resources. Overrides module-level or global error boundaries.Use the useRouteError hook to access error details within the component.

Return Type

Resource
Resource
A resource object that can be used in a module’s resources array or another resource’s subResources array.

Examples

Basic Resource

import { defineResource } from "@tailor-platform/app-shell";

const customPage = defineResource({
  path: "custom-page",
  component: () => {
    return (
      <div>
        <p>This is a custom page.</p>
      </div>
    );
  },
});

Resource with Metadata

import { defineResource } from "@tailor-platform/app-shell";
import { Package } from "lucide-react";

const productList = defineResource({
  path: "products",
  meta: {
    title: "Product List",
    icon: <Package />,
  },
  component: ({ title }) => <ProductList title={title} />,
});

Resource with Dynamic Segments

import { defineResource } from "@tailor-platform/app-shell";
import { useParams } from "react-router";

const productDetail = defineResource({
  path: ":productSlug",
  meta: { 
    title: "Product Details",
    breadcrumbTitle: (productSlug) => `Product ${productSlug}`
  },
  component: () => {
    const { productSlug } = useParams();
    return <ProductDetails productSlug={productSlug} />;
  },
});

Nested Resources (Sub-resources)

import { defineResource } from "@tailor-platform/app-shell";
import { useParams } from "react-router";

const productResource = defineResource({
  path: "products",
  meta: {
    title: "Product List",
    icon: <Package />,
  },
  component: ({ title }) => <ProductList title={title} />,
  subResources: [
    defineResource({
      path: ":productSlug",
      meta: { 
        title: "Product Details",
        breadcrumbTitle: (productSlug) => `Product ${productSlug}`
      },
      component: () => {
        const { productSlug } = useParams();
        return <ProductDetails productSlug={productSlug} />;
      },
      subResources: [
        defineResource({
          path: "edit",
          meta: { title: "Edit Product" },
          component: () => <ProductEditForm />
        })
      ]
    })
  ]
});

Resource with Guards

import { defineResource, pass, hidden } from "@tailor-platform/app-shell";

const adminSettings = defineResource({
  path: "admin-settings",
  component: AdminSettingsPage,
  guards: [
    async ({ context, signal }) => {
      const user = await getCurrentUser({ signal });
      return user.role === "admin" ? pass() : hidden();
    }
  ],
});

Resource with Error Boundary

import { defineResource, useRouteError } from "@tailor-platform/app-shell";

const CustomErrorBoundary = () => {
  const error = useRouteError() as Error;
  return (
    <div>
      <h1>Resource Error</h1>
      <p>{error.message}</p>
    </div>
  );
};

const riskySalesResource = defineResource({
  path: "risky-operation",
  component: RiskyOperationPage,
  errorBoundary: <CustomErrorBoundary />,
});

Resource with Internationalization

import { defineResource, defineI18nLabels } from "@tailor-platform/app-shell";

const labels = defineI18nLabels({
  en: {
    ordersTitle: "Orders",
  },
  ja: {
    ordersTitle: "注文",
  },
});

const ordersResource = defineResource({
  path: "orders",
  meta: {
    title: labels.t("ordersTitle"),
  },
  component: OrdersPage,
});

Usage in Modules

import { defineModule, defineResource } from "@tailor-platform/app-shell";

const salesModule = defineModule({
  path: "sales",
  component: SalesLandingPage,
  resources: [
    defineResource({
      path: "invoices",
      component: InvoicesPage,
    }),
    defineResource({
      path: "orders",
      component: OrdersPage,
      subResources: [
        defineResource({
          path: ":id",
          component: OrderDetailPage,
        }),
      ],
    }),
  ],
});

Notes

  • The component prop is required for resources (unlike modules where it’s optional)
  • Sub-resources do not appear in the sidebar navigation but are accessible via routing and appear in breadcrumbs
  • Dynamic path segments (:paramName) are accessible in the component using useParams() from react-router
  • Resources inherit the module’s error boundary unless they define their own
  • Guards are executed before the component renders during the loader phase

Build docs developers (and LLMs) love