# Sub-Pages — Tabs Component > **Part of:** [Sub-Pages guide](../sub-pages.md) > **See also:** [Overview](01-overview.md) | [Data Hook](02-data-hook.md) ## ⚠️ MANDATORY: Create This BEFORE Settings > **The Tabs component (`Tabs.tsx`) must be created BEFORE any Settings component.** > > The Tabs component is the **parent** that: > - Extracts the entity ID from the URL > - Fetches entity data via the data hook > - Passes props to the Settings component > > Without `Tabs.tsx`, a `Settings.tsx` file is useless — it cannot be rendered or receive data. > > **Creation order:** > 1. `useSettingsData.tsx` — Data hook (see [02-data-hook.md](02-data-hook.md)) > 2. `Settings.tsx` — Settings component (see [04-settings-tab.md](04-settings-tab.md)) > 3. `Tabs.tsx` — Tabs component (this file) ## Prerequisites Creating a Tabs component alone is not enough for navigation to work. You must also: 1. **Register the route** in `AppRoutes.tsx` 2. **Enable `showLink={true}`** in the main page's `MainTable` See [04-settings-tab.md](04-settings-tab.md#prerequisites--required-files-for-navigation) for the complete checklist. --- ## What is the Tabs Component? The Tabs component (`Tabs.tsx`) is the **entry point** for every sub-page. When a user clicks a row in a main page table, React Router navigates to this component. It acts as the "shell" that: 1. **Extracts the entity ID** from the URL (e.g., `/dns-zones/example.com.` → `example.com.`) 2. **Fetches entity data** using a custom data hook 3. **Renders breadcrumb navigation** so users know where they are 4. **Orchestrates tab switching** between Settings, membership, or custom tabs 5. **Handles loading and error states** (spinner while fetching, "Not Found" for missing entities) Think of it as the "controller" that coordinates data flow between the URL, the API, and the individual tab components. ## Component Lifecycle ``` User clicks row in main page ↓ React Router loads Tabs ↓ useSafeParams extracts entity ID from URL ↓ Custom data hook fetches entity from API ↓ While loading → show DataSpinner ↓ If not found → show NotFound page ↓ If found → render Tabs with entity data ``` ## Required Imports **Note:** All imports must be absolute (starting with `src/`). See [00-best-practices.md § Use Absolute Imports](00-best-practices.md#6-use-absolute-imports). ```tsx import React from "react"; import { PageSection, Tabs, Tab, TabTitleText } from "@patternfly/react-core"; import { useNavigate } from "react-router"; import { NotFound } from "src/components/errors/PageErrors"; import { useSettings } from "src/hooks/useSettingsData"; import TitleLayout from "src/components/layouts/TitleLayout"; import DataSpinner from "src/components/layouts/DataSpinner"; import BreadCrumb, { BreadCrumbItem } from "src/components/layouts/BreadCrumb/BreadCrumb"; import Settings from "src/pages//Settings"; import { useSafeParams } from "src/utils/paramsUtils"; ``` ## URL Parameter Extraction Every sub-page needs to know which entity to display. The entity ID comes from the URL path. We use `useSafeParams` (a type-safe wrapper around React Router's `useParams`) to extract it. ```tsx type Params = { : string }; const Tabs = ({ section }: TabsProps) => { const { } = useSafeParams<Params>([""]); // ... }; ``` **Common parameter names by entity type:** | Entity Type | Parameter | Example URL | |-------------|-----------|-------------| | Users | `uid` | `/active-users/admin` | | Groups/Rules | `cn` | `/user-groups/admins` | | Hosts | `fqdn` | `/hosts/server.example.com` | | DNS Zones | `idnsname` | `/dns-zones/example.com.` | | Services | `id` | `/services/HTTP%2Fserver.example.com` | ## State Management The component maintains several pieces of state: ```tsx // Breadcrumb navigation items const [breadcrumbItems, setBreadcrumbItems] = React.useState([]); // Local copy of entity ID (for convenience in navigation) const [id, setId] = React.useState(""); // All entity data comes from the custom hook const SettingsData = useSettingsData(); ``` The data hook (covered in [02-data-hook.md](02-data-hook.md)) handles all API communication and returns the entity data, loading state, and helper functions. ## Tab Navigation For multi-tab sub-pages, you need a handler that updates the URL when users switch tabs. This keeps the browser URL in sync with the active tab, enabling direct linking and browser back/forward navigation. ```tsx const handleTabClick = ( _event: React.MouseEvent, tabIndex: number | string ) => { if (tabIndex === "settings") { navigate("/" + pathname + "/" + id); } else if (tabIndex === "dns-records") { navigate("/" + pathname + "/" + id + "/dns-records"); } }; ``` ## Breadcrumb Setup Breadcrumbs help users understand their location in the app hierarchy. They're updated via `useEffect` whenever the entity ID changes: ```tsx React.useEffect(() => { setId(); setBreadcrumbItems([ { name: "", url: "/" + pathname }, { name: , url: "/" + pathname + "/" + , isActive: true }, ]); }, []); ``` ## Loading and Error States Always handle these states before rendering the main content: ```tsx // Show spinner while data is being fetched if (SettingsData.isLoading || !SettingsData.) { return ; } // Show "Not Found" if the entity doesn't exist if (!SettingsData.isLoading && Object.keys(SettingsData.).length === 0) { return ; } ``` ## JSX Structure The render output has two main sections: 1. **Header area** — breadcrumbs and page title 2. **Tabs area** — the PatternFly Tabs component with tab content ```tsx return ( <> Settings}> <Settings ={SettingsData.} original={SettingsData.original} metadata={SettingsData.metadata} onChange={SettingsData.set} onRefresh={SettingsData.refetch} isModified={SettingsData.modified} modifiedValues={SettingsData.modifiedValues} onResetValues={SettingsData.resetValues} pathname={pathname} /> {/* Additional tabs go here */} ); ``` **Key Tabs props:** - `activeKey={section}` — which tab is currently active (from URL) - `mountOnEnter` / `unmountOnExit` — lazy-load tabs for performance - `variant="secondary"` / `isBox` — PatternFly styling **Tab titles:** Use simple `` without `Badge` components. Badges (showing counts) are only used in the inner `TabLayout` within membership tab content components, not in the outer `Tabs.tsx`. ## Single-Tab vs Multi-Tab **Single-tab** (Settings only): The tab navigation handler only needs to handle one case. See `IdpReferencesTabs.tsx`. **Multi-tab**: Add more `` elements and extend the navigation handler. See `DnsZonesTabs.tsx` or `HostsTabs.tsx`. ## Examples | Pattern | Example File | |---------|--------------| | Single-tab (Settings only) | `src/pages/IdPReferences/IdpReferencesTabs.tsx` | | Multi-tab (Settings + Table) | `src/pages/DNSZones/DnsZonesTabs.tsx` | | Multi-tab (Settings + Membership) | `src/pages/Hosts/HostsTabs.tsx` |