TableOfContents

The TableOfContents component automatically discovers headings on the page (h1–h6) and renders a navigable outline with active section tracking. It supports scroll‑spy highlighting, deep-link copying, custom initial data (SSR / virtual docs), indentation control, appearance variants, and external active state notifications.

Basics

Drop a table of contents beside your main article and it will register headings automatically through the shared title registry.

Loading demo…

import

 

{

useRef

}

 

from

 

'react'

;

import

 

{

 

Block

,

 

Row

,

 

TableOfContents

,

 

Text

,

 

Title

,

 

TitleRegistryProvider

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

const

 

SECTIONS

 

=

 

[

  

{

id

:

 

'intro'

,

title

:

 

'Introduction'

,

summary

:

 

'Set the stage for the walkthrough.'

 

}

,

  

{

id

:

 

'setup'

,

title

:

 

'Setup'

,

summary

:

 

'Install dependencies and initialize the provider.'

 

}

,

  

{

id

:

 

'usage'

,

title

:

 

'Usage'

,

summary

:

 

'Render headings inside your content area to register them.'

 

}

,

  

{

id

:

 

'faq'

,

title

:

 

'FAQ'

,

summary

:

 

'Answer the questions you expect most often.'

 

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

const

contentRef

=

useRef

<HTMLDivElement

 

|

 

null

>

(

null

)

;

 

  

return

 

(

    

<TitleRegistryProvider

>

      

<Row

 

gap

=

"xl"

 

align

=

"flex-start"

>

        

<TableOfContents

          

container

=

{

contentRef

.

current

??

 

undefined

}

          

variant

=

"outline"

          

size

=

"sm"

          

p

=

"sm"

          

style

=

{

{

width

:

 

240

 

}

}

        

/>

        

<Block

 

ref

=

{

contentRef

}

 

component

=

"div"

 

grow

=

{

1

}

 

style

=

{

{

maxWidth

:

 

560

 

}

}

>

          

{

SECTIONS

.

map

(

(

section

,

index

)

 

=>

 

(

            

<Block

 

key

=

{

section

.

id

}

>

              

<Title

 

order

=

{

index

===

 

0

 

?

 

1

 

:

 

2

}

>

{

section

.

title

}

</Title

>

              

<Text

 

color

=

"secondary"

>

{

section

.

summary

}

</Text

>

            

</Block

>

          

)

)

}

        

</Block

>

      

</Row

>

    

</TitleRegistryProvider

>

  

)

;

}

Variants

Choose between the outline, ghost, filled, and none variants. Pair filled with autoContrast to keep labels legible against a brand color.

Loading demo…

import

 

{

 

Row

,

 

TableOfContents

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

const

 

ITEMS

 

=

 

[

  

{

id

:

 

'overview'

,

value

:

 

'Overview'

,

depth

:

 

1

 

}

,

  

{

id

:

 

'tokens'

,

value

:

 

'Color tokens'

,

depth

:

 

2

 

}

,

  

{

id

:

 

'accessibility'

,

value

:

 

'Accessibility'

,

depth

:

 

1

 

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

return

 

(

    

<Row

 

gap

=

"md"

 

align

=

"flex-start"

 

wrap

=

"wrap"

>

      

<TableOfContents

 

initialData

=

{

ITEMS

}

 

variant

=

"outline"

 

size

=

"xs"

 

style

=

{

{

width

:

 

200

 

}

}

 

/>

      

<TableOfContents

 

initialData

=

{

ITEMS

}

 

variant

=

"ghost"

 

size

=

"xs"

 

style

=

{

{

width

:

 

200

 

}

}

 

/>

      

<TableOfContents

        

initialData

=

{

ITEMS

}

        

variant

=

"filled"

        

color

=

"primary.6"

        autoContrast

        

size

=

"xs"

        

style

=

{

{

width

:

 

200

 

}

}

      

/>

    

</Row

>

  

)

;

}

Preloaded data

Seed the table of contents with initialData so servers and prerender jobs can render the navigation before headings mount.

Loading demo…

import

 

{

 

Block

,

 

TableOfContents

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

const

INITIAL_ITEMS

=

 

[

  

{

id

:

 

'overview'

,

value

:

 

'Overview'

,

depth

:

 

1

 

}

,

  

{

id

:

 

'setup'

,

value

:

 

'Setup'

,

depth

:

 

2

 

}

,

  

{

id

:

 

'usage'

,

value

:

 

'Usage'

,

depth

:

 

2

 

}

,

  

{

id

:

 

'advanced'

,

value

:

 

'Advanced'

,

depth

:

 

1

 

}

,

  

{

id

:

 

'faq'

,

value

:

 

'FAQ'

,

depth

:

 

1

 

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

return

 

(

    

<Block

 

align

=

"flex-start"

>

      

<TableOfContents

        

initialData

=

{

INITIAL_ITEMS

}

        

variant

=

"outline"

        

depthOffset

=

{

16

}

        

radius

=

"sm"

        

size

=

"sm"

        

p

=

"sm"

        

style

=

{

{

width

:

 

240

 

}

}

      

/>

    

</Block

>

  

)

;

}

Depth offset

Use minDepthToOffset and depthOffset to indent nested headings so deep sections are easy to scan.

Loading demo…

import

 

{

 

Block

,

 

TableOfContents

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

const

INITIAL_ITEMS

=

 

[

  

{

id

:

 

'intro'

,

value

:

 

'Introduction'

,

depth

:

 

1

 

}

,

  

{

id

:

 

'schedule'

,

value

:

 

'Release schedule'

,

depth

:

 

2

 

}

,

  

{

id

:

 

'api'

,

value

:

 

'API reference'

,

depth

:

 

2

 

}

,

  

{

id

:

 

'hooks'

,

value

:

 

'Hooks'

,

depth

:

 

3

 

}

,

  

{

id

:

 

'migration'

,

value

:

 

'Migration'

,

depth

:

 

1

 

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

return

 

(

    

<Block

 

align

=

"flex-start"

>

      

<TableOfContents

        

initialData

=

{

INITIAL_ITEMS

}

        

variant

=

"outline"

        

minDepthToOffset

=

{

2

}

        

depthOffset

=

{

28

}

        

size

=

"xs"

        

p

=

"sm"

        

style

=

{

{

width

:

 

240

 

}

}

      

/>

    

</Block

>

  

)

;

}

Active callbacks

Subscribe to onActiveChange to surface the currently highlighted section, perfect for syncing status chips or analytics.

Loading demo…

import

 

{

useRef

,

useState

}

 

from

 

'react'

;

import

 

{

 

Block

,

 

Chip

,

 

Row

,

 

TableOfContents

,

 

Text

,

 

Title

,

 

TitleRegistryProvider

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

const

 

SECTIONS

 

=

 

[

  

{

id

:

 

'overview'

,

title

:

 

'Overview'

,

summary

:

 

'Explain when the progress indicator should appear.'

 

}

,

  

{

id

:

 

'loading'

,

title

:

 

'Loading States'

,

summary

:

 

'Describe feedback while content is fetching.'

 

}

,

  

{

id

:

 

'error'

,

title

:

 

'Error Recovery'

,

summary

:

 

'Clarify what happens if the data fails to load.'

 

}

,

]

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

activeId

,

setActiveId

]

 

=

useState

<

string

 

|

 

null

>

(

null

)

;

  

const

contentRef

=

useRef

<HTMLDivElement

 

|

 

null

>

(

null

)

;

 

  

return

 

(

    

<TitleRegistryProvider

>

      

<Block

>

        

<Chip

 

variant

=

"light"

 

color

=

{

activeId

?

 

'primary'

 

:

 

'gray'

}

 

size

=

"sm"

>

          

Active

section

:

 

{

activeId

??

 

'None'

}

        

</Chip

>

 

        

<Row

 

gap

=

"xl"

 

align

=

"flex-start"

>

          

<TableOfContents

            

container

=

{

contentRef

.

current

??

 

undefined

}

            

variant

=

"outline"

            

size

=

"xs"

            

p

=

"sm"

            

style

=

{

{

width

:

 

240

 

}

}

            

onActiveChange

=

{

setActiveId

}

          

/>

          

<Block

 

ref

=

{

contentRef

}

 

component

=

"div"

 

grow

=

{

1

}

 

style

=

{

{

maxWidth

:

 

560

 

}

}

>

            

{

SECTIONS

.

map

(

(

section

,

index

)

 

=>

 

(

              

<Block

 

key

=

{

section

.

id

}

>

                

<Title

 

order

=

{

index

===

 

0

 

?

 

1

 

:

 

2

}

>

{

section

.

title

}

</Title

>

                

<Text

 

color

=

"secondary"

>

{

section

.

summary

}

</Text

>

              

</Block

>

            

)

)

}

          

</Block

>

        

</Row

>

      

</Block

>

    

</TitleRegistryProvider

>

  

)

;

}

Scan to open this page on your phonereact-ui-library.com/components/TableOfContents

</>

react-ui-library

100+ accessible, themeable components that work seamlessly across iOS, Android, and Web.

A Platform Blocks product.