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.

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.
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 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.
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.
- 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 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.mdAdd 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?
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 %}