@@ -26,6 +26,7 @@ import { focusIn, Focus } from '../../utils/focus-management'
2626import { useIsoMorphicEffect } from '../../hooks/use-iso-morphic-effect'
2727import { useSyncRefs } from '../../hooks/use-sync-refs'
2828import { useResolveButtonType } from '../../hooks/use-resolve-button-type'
29+ import { useLatestValue } from '../../hooks/use-latest-value'
2930
3031interface StateDefinition {
3132 selectedIndex : number | null
@@ -103,6 +104,9 @@ let TabsContext = createContext<
103104> ( null )
104105TabsContext . displayName = 'TabsContext'
105106
107+ let TabsSSRContext = createContext < MutableRefObject < number > | null > ( null )
108+ TabsSSRContext . displayName = 'TabsSSRContext'
109+
106110function useTabsContext ( component : string ) {
107111 let context = useContext ( TabsContext )
108112 if ( context === null ) {
@@ -147,14 +151,14 @@ let Tabs = forwardRefWithAs(function Tabs<TTag extends ElementType = typeof DEFA
147151
148152 let tabsRef = useSyncRefs ( ref )
149153 let [ state , dispatch ] = useReducer ( stateReducer , {
150- selectedIndex : null ,
154+ selectedIndex : typeof window === 'undefined' ? selectedIndex ?? defaultIndex : null ,
151155 tabs : [ ] ,
152156 panels : [ ] ,
153157 orientation,
154158 activation,
155159 } as StateDefinition )
156160 let slot = useMemo ( ( ) => ( { selectedIndex : state . selectedIndex } ) , [ state . selectedIndex ] )
157- let onChangeRef = useRef < ( index : number ) => void > ( ( ) => { } )
161+ let onChangeRef = useLatestValue ( onChange || ( ( ) => { } ) )
158162
159163 useEffect ( ( ) => {
160164 dispatch ( { type : ActionTypes . SetOrientation , orientation } )
@@ -164,12 +168,6 @@ let Tabs = forwardRefWithAs(function Tabs<TTag extends ElementType = typeof DEFA
164168 dispatch ( { type : ActionTypes . SetActivation , activation } )
165169 } , [ activation ] )
166170
167- useEffect ( ( ) => {
168- if ( typeof onChange === 'function' ) {
169- onChangeRef . current = onChange
170- }
171- } , [ onChange ] )
172-
173171 useEffect ( ( ) => {
174172 if ( state . tabs . length <= 0 ) return
175173 if ( selectedIndex === null && state . selectedIndex !== null ) return
@@ -225,15 +223,19 @@ let Tabs = forwardRefWithAs(function Tabs<TTag extends ElementType = typeof DEFA
225223 [ state , dispatch ]
226224 )
227225
226+ let SSRCounter = useRef ( 0 )
227+
228228 return (
229- < TabsContext . Provider value = { providerBag } >
230- { render ( {
231- props : { ref : tabsRef , ...passThroughProps } ,
232- slot,
233- defaultTag : DEFAULT_TABS_TAG ,
234- name : 'Tabs' ,
235- } ) }
236- </ TabsContext . Provider >
229+ < TabsSSRContext . Provider value = { typeof window === 'undefined' ? SSRCounter : null } >
230+ < TabsContext . Provider value = { providerBag } >
231+ { render ( {
232+ props : { ref : tabsRef , ...passThroughProps } ,
233+ slot,
234+ defaultTag : DEFAULT_TABS_TAG ,
235+ name : 'Tabs' ,
236+ } ) }
237+ </ TabsContext . Provider >
238+ </ TabsSSRContext . Provider >
237239 )
238240} )
239241
@@ -414,6 +416,11 @@ let Panel = forwardRefWithAs(function Panel<TTag extends ElementType = typeof DE
414416 ref : Ref < HTMLElement >
415417) {
416418 let [ { selectedIndex, tabs, panels } , { dispatch } ] = useTabsContext ( 'Tab.Panel' )
419+ let SSRContext = useContext ( TabsSSRContext )
420+
421+ if ( SSRContext !== null && selectedIndex === null ) {
422+ selectedIndex = 0 // Should normally not happen, but in case the selectedIndex is null, we can default to 0.
423+ }
417424
418425 let id = `headlessui-tabs-panel-${ useId ( ) } `
419426 let internalPanelRef = useRef < HTMLElement > ( null )
@@ -428,7 +435,8 @@ let Panel = forwardRefWithAs(function Panel<TTag extends ElementType = typeof DE
428435 } , [ dispatch , internalPanelRef ] )
429436
430437 let myIndex = panels . indexOf ( internalPanelRef )
431- let selected = myIndex === selectedIndex
438+ let selected =
439+ SSRContext === null ? myIndex === selectedIndex : SSRContext . current ++ === selectedIndex
432440
433441 let slot = useMemo ( ( ) => ( { selected } ) , [ selected ] )
434442 let propsWeControl = {
0 commit comments