@@ -3,6 +3,7 @@ import { cn } from "@fern-docs/components/cn";
33import { useCurrentAnchor } from "@fern-docs/components/hooks/use-anchor" ;
44import { useProgrammingLanguage } from "@fern-docs/components/state/language" ;
55import * as RadixTabs from "@radix-ui/react-tabs" ;
6+ import { usePathname , useRouter , useSearchParams } from "next/navigation" ;
67import { type ReactNode , useEffect , useState } from "react" ;
78
89import { unwrapChildren } from "../../common/unwrap-children" ;
@@ -19,15 +20,57 @@ export interface TabProps {
1920export interface TabGroupProps {
2021 toc ?: boolean ;
2122 className ?: string ;
23+ /**
24+ * the query parameter name to use for tab selection (e.g., "tab")
25+ * when set, the selected tab will be synced with the URL query parameter
26+ */
27+ paramName ?: string ;
2228}
2329
24- export function TabGroup ( { children, className } : { toc ?: boolean ; children ?: ReactNode ; className ?: string } ) {
30+ export function TabGroup ( {
31+ children,
32+ className,
33+ paramName
34+ } : {
35+ toc ?: boolean ;
36+ children ?: ReactNode ;
37+ className ?: string ;
38+ /**
39+ * the query parameter name to use for tab selection (e.g., "tab")
40+ * when set, the selected tab will be synced with the URL query parameter
41+ */
42+ paramName ?: string ;
43+ } ) {
2544 const items = unwrapChildren ( children , Tab ) ;
2645
27- const [ activeTab , setActiveTab ] = useState ( ( ) => items [ 0 ] ?. props . id ) ;
46+ const router = useRouter ( ) ;
47+ const pathname = usePathname ( ) ;
48+ const searchParams = useSearchParams ( ) ;
49+ const tabParam = paramName ? searchParams . get ( paramName ) : null ;
50+
51+ const [ activeTab , setActiveTab ] = useState ( ( ) => {
52+ // Priority: query param > first tab
53+ if ( tabParam != null ) {
54+ const matchingTab = items . find ( ( item ) => item . props . id === tabParam ) ;
55+ if ( matchingTab ) {
56+ return tabParam ;
57+ }
58+ }
59+ return items [ 0 ] ?. props . id ;
60+ } ) ;
2861 const anchor = useCurrentAnchor ( ) ;
2962 const [ selectedLanguage , setSelectedLanguage ] = useProgrammingLanguage ( ) ;
3063
64+ // Sync with query parameter when it changes
65+ useEffect ( ( ) => {
66+ if ( tabParam != null ) {
67+ const matchingTab = items . find ( ( item ) => item . props . id === tabParam ) ;
68+ if ( matchingTab ) {
69+ setActiveTab ( tabParam ) ;
70+ }
71+ }
72+ } , [ tabParam , items ] ) ;
73+
3174 useEffect ( ( ) => {
3275 if ( anchor != null ) {
3376 if ( items . some ( ( item ) => item . props . id === anchor ) ) {
@@ -58,6 +101,15 @@ export function TabGroup({ children, className }: { toc?: boolean; children?: Re
58101
59102 const handleTabChange = ( tabId : string ) => {
60103 setActiveTab ( tabId ) ;
104+
105+ // Update URL with query parameter when paramName is specified
106+ if ( paramName ) {
107+ const params = new URLSearchParams ( searchParams . toString ( ) ) ;
108+ params . set ( paramName , tabId ) ;
109+ const newURL = `${ pathname } ?${ params . toString ( ) } ` ;
110+ router . replace ( newURL , { scroll : false } ) ;
111+ }
112+
61113 const selectedTab = items . find ( ( item ) => item . props . id === tabId ) ;
62114 const cleanedLanguage = selectedTab ?. props . language
63115 ? ApiDefinition . cleanLanguage ( selectedTab . props . language )
0 commit comments