Common types for Docusaurus packages.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Component customization system for safely ejecting and wrapping theme components.
Configuration for component swizzling that defines which components can be customized and how.
/**
* Configuration for component swizzling
*/
interface SwizzleConfig {
/** Configuration for each swizzleable component */
components: {[componentName: string]: SwizzleComponentConfig};
}
/**
* Configuration for an individual swizzleable component
*/
interface SwizzleComponentConfig {
/** Actions allowed for this component and their safety status */
actions: {[action in SwizzleAction]: SwizzleActionStatus};
/** Optional description of the component for documentation */
description?: string;
}Usage Example:
import type { SwizzleConfig } from '@docusaurus/types';
const themeSwizzleConfig: SwizzleConfig = {
components: {
'Navbar': {
actions: {
eject: 'safe',
wrap: 'safe'
},
description: 'Main navigation bar component'
},
'Footer': {
actions: {
eject: 'safe',
wrap: 'safe'
},
description: 'Site footer component'
},
'SearchBar': {
actions: {
eject: 'unsafe',
wrap: 'safe'
},
description: 'Search functionality - wrapping is preferred'
},
'DocPaginator': {
actions: {
eject: 'forbidden',
wrap: 'unsafe'
},
description: 'Internal pagination logic - customization not recommended'
}
}
};Types defining the available swizzling actions and their safety levels.
/**
* Available swizzling actions
*/
type SwizzleAction = 'eject' | 'wrap';
/**
* Safety status for swizzling actions
*/
type SwizzleActionStatus = 'safe' | 'unsafe' | 'forbidden';Action Descriptions:
eject: Copies the component to your site for full customizationwrap: Creates a wrapper component that can enhance the originalStatus Descriptions:
safe: Recommended for customization, unlikely to break in updatesunsafe: Possible but may break in future versionsforbidden: Should not be customized, may cause issuesHow plugins and themes provide swizzle configuration:
import type { PluginModule, SwizzleConfig } from '@docusaurus/types';
const themeClassic: PluginModule = (context, options) => {
return {
name: 'docusaurus-theme-classic',
getThemePath() {
return path.resolve(__dirname, './theme');
},
};
};
// Provide swizzle configuration
themeClassic.getSwizzleConfig = (): SwizzleConfig => ({
components: {
'Navbar': {
actions: {
eject: 'safe',
wrap: 'safe'
},
description: 'Main site navigation component'
},
'Footer': {
actions: {
eject: 'safe',
wrap: 'safe'
},
description: 'Site footer with links and copyright'
},
'Layout': {
actions: {
eject: 'unsafe',
wrap: 'safe'
},
description: 'Main layout wrapper - wrapping recommended'
}
}
});
export default themeClassic;Advanced TypeScript utility for extracting component props in wrap swizzling.
/**
* Utility type for extracting component props with proper handling of
* function components with no parameters
*/
type WrapperProps<
T extends keyof JSX.IntrinsicElements | JSXElementConstructor<any>,
> = T extends JSXElementConstructor<infer P>
? unknown extends P
? {}
: P
: T extends keyof JSX.IntrinsicElements
? JSX.IntrinsicElements[T]
: {};Usage Example:
import React from 'react';
import OriginalNavbar from '@theme-original/Navbar';
import type { WrapperProps } from '@docusaurus/types';
// Extract props from original component
type Props = WrapperProps<typeof OriginalNavbar>;
export default function NavbarWrapper(props: Props): JSX.Element {
return (
<>
<div className="custom-navbar-header">Custom Header</div>
<OriginalNavbar {...props} />
<div className="custom-navbar-footer">Custom Footer</div>
</>
);
}# List available components
npm run swizzle @docusaurus/theme-classic
# Eject a component (copies to src/theme/)
npm run swizzle @docusaurus/theme-classic Navbar -- --eject
# Wrap a component (creates wrapper in src/theme/)
npm run swizzle @docusaurus/theme-classic Navbar -- --wrap
# Check safety status
npm run swizzle @docusaurus/theme-classic Navbar -- --listWhen you eject a component, it's copied to your site:
src/
└── theme/
├── Navbar/
│ ├── index.tsx # Main component
│ ├── ColorModeToggle/
│ │ └── index.tsx # Sub-component
│ └── styles.module.css # Component styles
└── Footer/
└── index.tsxWrapping allows you to enhance components without copying them:
// src/theme/Navbar/index.tsx (wrapper)
import React from 'react';
import OriginalNavbar from '@theme-original/Navbar';
import { useLocation } from '@docusaurus/router';
import type { WrapperProps } from '@docusaurus/types';
type Props = WrapperProps<typeof OriginalNavbar>;
export default function NavbarWrapper(props: Props): JSX.Element {
const location = useLocation();
// Add custom logic
const isHomePage = location.pathname === '/';
return (
<div className={isHomePage ? 'navbar-home' : 'navbar-default'}>
{/* Custom banner on home page */}
{isHomePage && (
<div className="home-banner">Welcome to our site!</div>
)}
{/* Original navbar with all original functionality */}
<OriginalNavbar {...props} />
{/* Custom footer */}
<div className="navbar-custom-footer">
Current page: {location.pathname}
</div>
</div>
);
}// ✅ Good: Wrap components to add functionality
export default function EnhancedSearchBar(props: Props) {
return (
<div className="search-wrapper">
<OriginalSearchBar {...props} />
<div className="search-shortcuts">
<kbd>Ctrl</kbd> + <kbd>K</kbd>
</div>
</div>
);
}
// ✅ Good: Eject safe components for major customization
export default function CustomNavbar({ items }: Props) {
return (
<nav className="completely-custom-navbar">
{/* Completely custom implementation */}
{items.map(item => (
<CustomNavItem key={item.to} item={item} />
))}
</nav>
);
}
// ❌ Avoid: Ejecting unsafe components
// This might break in future updates
export default function CustomLayout({ children }: Props) {
// Complex internal logic that might change
return <div>{children}</div>;
}import React from 'react';
import type { Props } from '@theme/Navbar'; // Use theme types
import OriginalNavbar from '@theme-original/Navbar';
// Type-safe wrapper
export default function NavbarWrapper(props: Props): JSX.Element {
// TypeScript will ensure props match the original component
return (
<div className="navbar-enhanced">
<OriginalNavbar {...props} />
</div>
);
}// Start with wrapping for minimal changes
export default function NavbarWrapper(props: Props) {
return (
<>
<OriginalNavbar {...props} />
<CustomSearchShortcuts />
</>
);
}
// Later, if more customization is needed, consider ejecting
// But check if the component is still marked as "safe"Install with Tessl CLI
npx tessl i tessl/npm-docusaurus--types