Eject components tutorial
Update tabs component behavior
Let's imagine you are writing a tutorial describing how to write code to work with your product. You decided to use Redocly's tabs Markdoc tag to show code examples in different languages. As you are working, you decide it would be nice if a user could select their preferred programming language once triggering the update of all other tabs on the page to the selected programming language.
But you notice, the Redocly tabs Markdoc tag, does not have the ability to configure syncing between tabs. So you decide to eject the component to update the behavior to allow configuring syncing between tabs.

Eject the tabs component in Reunite
For this tutorial we are going to eject the component using the Reunite editor. To eject the component using the Reunite editor:
- Select Editor on the left side navigation.
- Click the Theme components in the Files section.
- Search for tabs.
- Find the Tabs.tsx component and click the eject icon.

After ejecting, the component is added to the @theme
folder at the path @theme/markdoc/components/Tabs/Tabs.tsx
where we can modify it.
Implement useTabs
hook
Create a new useTabs.ts
file adjacent to the Tabs.tsx
file and copy the code from the example. You can also download all examples and drag and drop them to the @theme/markdoc/components/Tabs
folder.
This custom hook reuses the original useTabs
hook and adds syncing functionality between tabs with the same syncId
. It stores the activeTab
state in localStorage
after an update was triggered, and all other tabs are subscribed to the storage events to update their state. Also, for initial mount, it gets the activeTab
from localStorage
to preserve the tabs state even after a page is reloaded.
This tutorial does not provide step-by-step instructions for every customization. Each case is unique and requires a different approach. React, Markdoc and TypeScript knowledge is required to customize it.
Replace the useTabs
hook inside the Tabs component
Replace useTabs hook import
Replace the original useTabs
hook import with the custom hook.
Extend TabsProps interface
Extend the TabsProps
interface to include the syncId
prop.
Add syncId prop to the Tabs component
Add the syncId
prop to the Tabs
component.
Pass syncId to the useTabs hook
And finally, pass the syncId
prop to the useTabs
hook.
Extend the tabs Markdoc schema
We also need to update the tabs interface to be able to pass the syncId
prop to the tabs
Markdoc component, which passes the syncId
to our modified Tabs
React component.
Eject the tags Markdoc schema
- Select Editor on the left side navigation.
- Click the Theme components in the Files section.
- Search for tabs.
- Find the tabs.ts tag and click the eject icon.
Add syncId markdoc attribute
Add the syncId
Markdoc attribute to the tabs
tag.
Fix "invalid attribute: 'syncId'" warning
To fix a warning, let's redefine our Markdoc schema. Create a new schema.ts
inside the @theme/markdoc
folder and copy the code from the example.
Create a tutorial page
Create tutorial page
To test our changes, let's create a tutorial.md
page in the root of the project and copy the code from the example.
Your file structure should look like this:
├──@theme/
│ └──markdoc/
│ ├──components/
│ │ └──Tabs/
│ │ ├──Tabs.tsx
│ │ └──useTabs.ts
│ ├──tags/
│ │ └──tabs.ts
│ └──schema.ts
└──tutorial.md
Add syncId attribute
Since we added syncId
attributes to the tabs
tags, we can sync the tabs state between those tabs with the same syncId
.
Test it out. Do the tabs sync?
🎉 Congratulations
You have successfully ejected the Tabs
component and added custom functionality to it. Don't forget, with great power comes great responsibility, now you are responsible for maintaining the component 😉.
# Tutorial
## Declare a variable
{% tabs syncId="programming-language" %}
{% tab label="JavaScript" %}
```js
const arr = [];
```
{% /tab %}
{% tab label="TypeScript" %}
```ts
const arr: number[] = [];
```
{% /tab %}
{% /tabs %}
## Define an async function
{% tabs syncId="programming-language" %}
{% tab label="JavaScript" %}
```js
async function promisify(arg) {
return arg;
}
```
{% /tab %}
{% tab label="TypeScript" %}
```ts
async function promisify<T>(arg: T): Promise<T> {
return arg;
}
```
{% /tab %}
{% /tabs %}