Complete Guide to Tabs in Astro MDX
Create beautiful, feature-rich tabs in your Astro MDX files with full Markdown support, syntax highlighting, callouts, tables, and more. This component works seamlessly with Astro’s React integration and supports both light and dark themes.
✨ Key Features
🎨 Real-time theme switching (light/dark mode)
📝 Full Markdown support (headers, lists, links, tables)
💻 Expressive Code integration with syntax highlighting
🔔 Callouts/Admonitions (:::tip
, :::warning
, etc.)
🎯 Default tab selection
🏷️ Auto-generated tab names when labels aren’t provided
🔄 Backward compatibility (use Tab
or TabItem
)
⚡ Zero configuration - just import and use
1. Import the Components
import Tabs, { TabItem } from "@/react/Tabs/Tabs.jsx" ;
// or use backward compatible import
import Tabs, { Tab } from "@/react/Tabs/Tabs.jsx" ;
or with relative path:
import Tabs, { TabItem } from "../../react/Tabs/Tabs.jsx" ;
// or use backward compatible import
import Tabs, { Tab } from "../../react/Tabs/Tabs.jsx" ;
Important: Use .mdx
extension (not .md
) when working with React components.
2. Basic Usage
Your First Tabs This is how easy it is to create tabs! You can use:
Bold and italic text
inline code
Links
Lists and more Markdown features
💡 Pro Tip: Always include client:load
for interactivity in Astro!
Use Tab or TabItem Both of these work exactly the same:
New way (recommended):
< Tabs defaultValue = "example" client : load >
< TabItem value = "example" label = "My Tab" >
< TabItem value = "basic" label = "My Tab 2" >
Old way (still supported):
< Tabs defaultValue = "example" client : load >
< Tab value = "example" label = "My Tab" >
< Tab value = "basic" label = "My Tab 2" >
What You Need For Tabs component:
client:load
- Makes tabs interactive in Astro
defaultValue
- Which tab to show first (optional)
For each TabItem:
value
- Unique ID for the tab
label
- Text shown on the tab button
Content goes between opening/closing tags
That’s it! Super simple to get started.
📚 Complete Feature Guide
3. Code Syntax Highlighting with Expressive Code
Our tabs work seamlessly with Astro’s Expressive Code for beautiful syntax highlighting:
async function fetchUserData ( userId ) {
const response = await fetch ( `/api/users/${ userId }` );
const data = await response. json (); // Added error handling
throw new Error ( 'Failed to fetch user data' ); // Remove this old approach
const user = await fetchUserData ( 123 );
console. log ( 'User:' , user);
console. error ( 'Error:' , error.message);
interface ApiResponse < T > {
status : 'success' | 'error' ;
// Generic function with proper typing
async function apiCall < T >( url : string ) : Promise < ApiResponse < T >> {
const response = await fetch (url);
return await response. json () as ApiResponse < T >;
// Usage with type safety
const userResponse = await apiCall < User []>( '/api/users' );
from typing import List, Dict, Optional
from dataclasses import dataclass
from datetime import datetime
created_at: datetime = datetime.now()
async def fetch_users (session: aiohttp.ClientSession) -> List[User]:
"""Fetch users from API asynchronously."""
async with session.get( '/api/users' ) as response:
data = await response.json()
return [User( ** user_data) for user_data in data[ 'users' ]]
async with aiohttp.ClientSession() as session:
users = await fetch_users(session)
active_users = [u for u in users if u.is_active]
print ( f "Found {len (active_users) } active users" )
if __name__ == "__main__" :
4. Markdown Features Inside Tabs
Full Markdown support means you can use everything from headers to tables:
Text Styling Options You can use all standard Markdown formatting:
Bold text for emphasis
Italic text for subtle emphasis
inline code
for technical terms
Strikethrough for corrections
Links to anywhere
Lists Work Great Unordered lists:
First item
Second item
Nested items work too
Multiple levels supported
Third item
Ordered lists:
Step one
Step two
Step three
Task lists:
Blockquotes and Callouts
This is a standard blockquote. Great for highlighting important information or featuring quotes from users and documentation.
Using Callouts (if supported by your setup):
✅ Success
You’ve successfully created interactive tabs with full Markdown support!
Tables and Complex Content Feature Comparison
Feature Basic HTML Our Tabs Notes Markdown Support ❌ No✅ YesFull MDX compatibility Syntax Highlighting ❌ No✅ YesExpressive Code integration Theme Switching ❌ No✅ YesReal-time, no refresh needed Mobile Responsive ⚠️ Manual✅ YesBuilt-in responsive design Copy Code Button ❌ No✅ YesAutomatic with Expressive Code Line Numbers ❌ No✅ YesConfigurable per code block
5. Smart Tab Configuration
Tabs come with intelligent defaults and automatic features:
Setting Default Active Tab Use the defaultValue
prop to specify which tab opens first:
< Tabs defaultValue = "second" client : load >
< TabItem value = "first" label = "First Tab" >
This won't be shown initially
< TabItem value = "second" label = "Second Tab" >
This tab will be active when the page loads! 🎉
< TabItem value = "third" label = "Third Tab" >
Pro Tips:
If no defaultValue
is specified, the first tab will be active
Make sure your defaultValue
matches one of your TabItem value
props
This works great for highlighting the most important content first
Tabs Without Manual Labels & Values For quick prototyping, you can omit labels & values and get auto-generated names:
This tab will automatically be labeled "Tab 1"
This becomes "Tab 2" automatically
When to use auto-names:
✅ Quick prototyping and testing
✅ When content is self-explanatory
❌ Production apps (users need clear labels)
❌ Accessibility-critical applications
Accessibility Note: Always provide meaningful labels for production use to ensure screen readers can properly navigate your tabs.
🚀 Live Examples
Try these interactive examples to see tabs in action!
Multi-Language Code Examples
const grade = getGrade (score);
function getGrade ( score ) {
} else if (score >= 80 ) {
} else if (score >= 70 ) {
return "C - Satisfactory" ;
return "F - Needs improvement" ;
console. log ( `Score: ${ score }, Grade: ${ grade }` );
// Output: Score: 85, Grade: B - Good job!
def check_number_type (num):
"""Check if a number is even or odd."""
return f " { num } is an even number"
return f " { num } is an odd number"
user_input = int ( input ( "Enter a number: " ))
result = check_number_type(user_input)
print ( "Please enter a valid integer!" )
# Test with multiple numbers
test_numbers = [ 1 , 2 , 3 , 4 , 5 , 10 , 15 , 20 ]
print (check_number_type(num))
public class HelloWorld {
public static void main ( String [] args ) {
// Simple greeting program
String greeting = generateGreeting (name);
System.out. println (greeting);
System.out. println ( "Welcome to Java programming!" );
private static String generateGreeting (String name ) {
return "Hello, " + name + "!" ;
// Welcome to Java programming!
add ( a : number , b : number ) : number ;
subtract ( a : number , b : number ) : number ;
multiply ( a : number , b : number ) : number ;
divide ( a : number , b : number ) : number ;
class BasicCalculator implements Calculator {
add ( a : number , b : number ) : number {
subtract ( a : number , b : number ) : number {
multiply ( a : number , b : number ) : number {
divide ( a : number , b : number ) : number {
throw new Error ( "Division by zero is not allowed" );
const calc = new BasicCalculator ();
console. log (calc. add ( 10 , 5 )); // 15
console. log (calc. multiply ( 4 , 3 )); // 12
Tabs with Auto-Generated Names
These tabs don’t have manual labels - they get “Tab 1”, “Tab 2”, etc. automatically:
First Auto-Named Tab This tab automatically gets labeled as “Tab 1” because no label
prop was provided.
Great for:
Quick prototyping
Testing content layout
When content is self-explanatory
Note: For production apps, always provide meaningful labels for better accessibility.
Second Auto-Named Tab This becomes “Tab 2” automatically.
Features demonstrated:
Auto-numbering system
Fallback label generation
Same functionality as labeled tabs
Content here... (no label prop)
Third Auto-Named Tab This becomes “Tab 3” and demonstrates:
Consistency: All tabs get sequential numbering regardless of content length or complexity.
Flexibility: You can mix auto-named and manually labeled tabs in the same set, though it’s not recommended for production.
Testing: Perfect for rapid prototyping when you’re focusing on content rather than navigation.
Fourth Example Even without any label
prop, this tab gets “Tab 4” automatically.
The auto-naming system ensures users can always distinguish between different tabs, even when developers forget to add labels during development.
// Minimal tab setup for testing:
< TabItem value = "one" >Content 1</ TabItem >
< TabItem value = "two" >Content 2</ TabItem >
< TabItem value = "three" >Content 3</ TabItem >
// Results in: "Tab 1", "Tab 2", "Tab 3"
🎯 Summary & Next Steps
Congratulations! You now have a complete understanding of the tabs system. Here’s what you’ve learned:
✅ Key Features Covered
Basic Usage - Import, setup, and essential props
Expressive Code Integration - Syntax highlighting, line numbers, and code features
Full Markdown Support - Text formatting, lists, tables, quotes, and callouts
Smart Configuration - Default tabs, auto-generated names, responsive design
Backward Compatibility - Both Tab and TabItem work identically
Best Practices - Performance tips, accessibility, and customization
🚀 Ready to Use
You’re now ready to create beautiful, interactive tabs in your Astro MDX blog posts! Start with the basic example and gradually add more advanced features as needed.
💡 Pro Tips for Success
Always include client:load
for interactivity in Astro
Provide meaningful labels for better user experience
Use defaultValue
to highlight your most important content
Test your tabs in both light and dark themes
Keep tab content focused and well-organized
Happy coding! 🎉