CodeBlock Component
Relevant source files
The following files were used as context for generating this wiki page:The CodeBlock component is a fundamental UI element in the railways-docs project, responsible for rendering syntax-highlighted code snippets. It is built upon the react-syntax-highlighter library and provides a rich set of features including multi-language support, light and dark mode theming, a copy-to-clipboard utility, and special directives for line highlighting and hiding.
This component is primarily used within the MDX rendering pipeline to display code from Markdown files. It replaces the standard HTML <pre> tag, enhancing the developer experience by providing interactive and visually appealing code examples throughout the documentation.
Sources: src/components/CodeBlock/index.tsx, src/pages/[...slug].tsx:46, src/pages/dynamic/[...slug].tsx:41
Component Architecture
The CodeBlock component is a React functional component that orchestrates several features. It takes props like className (which contains language information), children (the raw code string), and colorModeSSR for correct server-side theme rendering.
Its main responsibilities are:
- Parsing the language and other parameters from the
classNameprop. - Normalizing the code content by stripping out special directives using the
normalizehelper. - Applying the correct syntax highlighting theme based on the application's current color mode (light/dark).
- Rendering the "Copy to Clipboard" button and handling its state.
Sources: src/components/CodeBlock/index.tsx
Component Flow
The following diagram illustrates the data flow and key dependencies of the CodeBlock component.
js `] --> B{Page Component};
end
subgraph Page Component
direction LR
B --> C[useMDXComponent];
C --> D{componentsWithProps};
end
D --> E[CodeBlock Component];
subgraph CodeBlock Component
direction TB
E --> F[getParams];
E --> G[normalize];
E --> H[useTheme / colorModeSSR];
E --> I[useCopy];
F --> J[SyntaxHighlighter];
G --> J;
H --> J[SyntaxHighlighter];
I --> K[Copy Button];
end
J --> L[Rendered HTML];
K --> L;
style MDX Page fill:#f9f,stroke:#333,stroke-width:2px
style "Page Component" fill:#ccf,stroke:#333,stroke-width:2px
style "CodeBlock Component" fill:#cfc,stroke:#333,stroke-width:2px
*Sources: [src/components/CodeBlock/index.tsx](https://github.com/Alonso-pixel/railways-docs/blob/HEAD/src/components/CodeBlock/index.tsx), [src/pages/[...slug].tsx](), [src/pages/dynamic/[...slug].tsx]()*
### Props
The component accepts the following props:
| Prop | Type | Description | Source |
|--------------|----------------|---------------------------------------------------------------------------------------------------------|------------------------------------------------------------|
| `className` | `string` | Optional. Provided by MDX, contains the language and other parameters (e.g., `language-js:for=js`). | `[src/components/CodeBlock/index.tsx:70](https://github.com/Alonso-pixel/railways-docs/blob/HEAD/src/components/CodeBlock/index.tsx#L70)` |
| `language` | `string` | Optional. Explicitly sets the language for syntax highlighting. | `[src/components/CodeBlock/index.tsx:69](https://github.com/Alonso-pixel/railways-docs/blob/HEAD/src/components/CodeBlock/index.tsx#L69)` |
| `children` | `any` | The raw code string to be rendered. | `[src/components/CodeBlock/index.tsx:71](https://github.com/Alonso-pixel/railways-docs/blob/HEAD/src/components/CodeBlock/index.tsx#L71)` |
| `colorModeSSR`| `string \| null` | Optional. The color mode ('light' or 'dark') passed from the server to prevent theme flicker on load. | `[src/components/CodeBlock/index.tsx:72](https://github.com/Alonso-pixel/railways-docs/blob/HEAD/src/components/CodeBlock/index.tsx#L72), [src/pages/dynamic/[...slug].tsx:42]()` |
## Core Functionality
### Syntax Highlighting
Syntax highlighting is powered by `react-syntax-highlighter` using a light version of PrismJS (`PrismLight`). The component explicitly registers a set of supported languages to keep the bundle size optimized.
*Sources: [src/components/CodeBlock/index.tsx:3](https://github.com/Alonso-pixel/railways-docs/blob/HEAD/src/components/CodeBlock/index.tsx#L3), [package.json:44](https://github.com/Alonso-pixel/railways-docs/blob/HEAD/package.json#L44)*
#### Supported Languages
The following languages are registered and supported out-of-the-box:
| Language | Registration Alias(es) |
|--------------|------------------------|
| Bash | `bash` |
| Clojure | `clojure` |
| CSS | `css` |
| Docker | `docker` |
| Elixir | `elixir` |
| Go | `go` |
| GraphQL | `graphql` |
| Java | `java` |
| JavaScript | `js`, `javascript` |
| JSON | `json` |
| PHP | `php` |
| Python | `python` |
| Ruby | `ruby` |
| Rust | `rust` |
| Scala | `scala` |
| TOML | `toml` |
| TypeScript | `typescript` |
*Sources: [src/components/CodeBlock/index.tsx:29-46](https://github.com/Alonso-pixel/railways-docs/blob/HEAD/src/components/CodeBlock/index.tsx#L29-L46)*
### Content Normalization
Before rendering, the code content is processed by the `normalize` function from `src/components/CodeBlock/normalize.ts`. This function, inspired by a similar utility in Gatsby, is responsible for parsing special comment-based directives within the code.
It performs two main tasks:
1. **Strips directive comments**: Removes the directive syntax (e.g., `// highlight-line`) from the code so it isn't rendered.
2. **Identifies targeted lines**: Creates a lookup object that maps line numbers to a boolean indicating if they should be highlighted.
*Sources: [src/components/CodeBlock/normalize.ts:1-2, 64-67](https://github.com/Alonso-pixel/railways-docs/blob/HEAD/src/components/CodeBlock/normalize.ts#L1-L2)*
#### Normalization Flow
Supported Directives
The normalize function understands directives for highlighting and hiding lines of code.
| Directive | Purpose |
|---|---|
highlight-next-line | Highlights the line immediately following the directive comment. |
highlight-line | Highlights the line where the directive comment is located. |
highlight-start | Begins a multi-line block of highlighting. |
highlight-end | Ends a multi-line block of highlighting. |
hide-next-line | Hides the line immediately following the directive comment. |
hide-line | Hides the line where the directive comment is located. |
hide-start | Begins a multi-line block of hidden code. |
hide-end | Ends a multi-line block of hidden code. |
Sources: src/components/CodeBlock/normalize.ts:6-10
Theming
The CodeBlock component seamlessly integrates with the application's light and dark mode theming system.
- Server-Side Rendering (SSR): The
colorModeSSRprop ensures the correct theme is applied on the initial server-rendered page load, preventing a "flash of incorrect theme." This prop is passed down fromgetServerSidePropsindynamic/[...slug].tsx. - Client-Side Theming: On the client, the
useThemehook determines the current color mode. - Theme Styles: Based on the color mode, either
lightCodeThemeordarkCodeThemeis passed to theSyntaxHighlighter. These theme objects are imported fromsrc/styles/codeThemes.ts.
Additionally, base styles for <pre> and <code> elements are defined in tailwind.config.js under the typography plugin settings.
Sources: src/components/CodeBlock/index.tsx:23-24, 118-120, src/pages/dynamic/[...slug].tsx:29, 42, 62, tailwind.config.js:77-85
Interactivity
A "Copy to Clipboard" button appears on the top-right corner of the code block when the user hovers over it.
- Implementation: This is handled by the
useCopycustom hook. - UI: The button uses the
CopyandCheckCircleicons fromreact-featherto provide visual feedback. The button is only visible ongroup-hover.
// from src/components/CodeBlock/index.tsx
<div tw="absolute top-0 right-0 mr-1 mt-1 text-gray-300 hover:text-gray-400 hidden group-hover:flex">
{copied ? (
<div tw="p-1">
<Icon icon={CheckCircle} size="sm" />
</div>
) : (
<button
tw="focus:ring-0 hover:bg-gray-200 p-1 rounded-md"
type="button"
onClick={() => copy(content)}
>
<Icon icon={Copy} size="sm" />
</button>
)}
</div>
Sources: src/components/CodeBlock/index.tsx:2, 22, 100, 125-138
Usage in MDX
The CodeBlock component is designed to automatically handle fenced code blocks from MDX files. This integration is configured in the page components that render MDX content, such as src/pages/[...slug].tsx and src/pages/dynamic/[...slug].tsx.
In these files, the useMDXComponent hook is used to render the MDX content. A custom components object is passed to it, which maps the standard pre HTML element to our custom CodeBlock component.
// from src/pages/[...slug].tsx
export default function PostPage({ page, colorModeSSR, rawMarkdown }) {
const MDXContent = useMDXComponent(page.body.code);
// Create a new components object with the extra props
const componentsWithProps = {
...components,
pre: (props: CodeBlockProps) => (
<CodeBlock {...props} colorModeSSR={colorModeSSR} />
),
// ...
};
return (
<Layout /* ... */ >
<MDXContent components={componentsWithProps} />
</Layout>
);
}
Sources: src/pages/[...slug].tsx:38-55, src/pages/dynamic/[...slug].tsx:33-50
Parameter Parsing
The language for syntax highlighting is parsed from the className prop, which is automatically generated by the MDX processor (e.g., ```javascript becomes className="language-javascript"). The getParams utility function in CodeBlock/index.tsx handles this parsing.
Sources: src/components/CodeBlock/index.tsx:75-96
Dependencies
The CodeBlock functionality relies on several key packages defined in package.json.
| Package | Version | Description |
|---|---|---|
react-syntax-highlighter | ^15.5.0 | Core library for syntax highlighting. |
prismjs | ^1.29.0 | The underlying syntax highlighting engine used by react-syntax-highlighter. |
contentlayer | ^0.3.4 | Processes Markdown/MDX files into data that can be used in the app. |
next-contentlayer | ^0.3.4 | Provides the useMDXComponent hook to render MDX content in Next.js. |
react-feather | ^2.0.9 | Provides icons for the copy-to-clipboard button. |
twin.macro | ^3.1.0 | Used for styling the component with Tailwind CSS in a CSS-in-JS manner. |
Sources: package.json:30, 34, 42, 44, 46, 51