Introduction Link to heading
Static site generators like Hugo are fantastic for creating fast, SEO-friendly websites. However, sometimes you need interactive components that go beyond static HTML. In this post, I’ll show you how to seamlessly integrate React components into your Hugo blog.
Why Combine Hugo and React? Link to heading
Hugo gives you:
- ⚡ Lightning-fast build times
- 🔍 Great SEO out of the box
- 📝 Simple content management with Markdown
React adds:
- 🎨 Interactive UI components
- ⚛️ Component-based architecture
- 🔄 Dynamic state management
Together, they create a powerful hybrid approach: static content with dynamic islands of interactivity.
Interactive Examples Link to heading
Example 1: Counter Component Link to heading
Here’s a simple interactive counter built with React:
The counter above is a fully functional React component embedded in this static Hugo page!
Example 2: Todo List Application Link to heading
A more complex example - a fully functional todo list:
How It Works Link to heading
The integration uses Babel.js to transpile JSX during the Hugo build process, following best practices:
Setup Steps Link to heading
Install Babel dependencies:
npm init -y npm install @babel/cli @babel/core @babel/preset-react --saveCreate
babel.config.jsin your Hugo root:module.exports = function (api) { api.cache(true); const presets = [["@babel/preset-react"]]; const plugins = []; return { presets, plugins }; }Create React components in
/assets/js/react-apps/:const { useState } = React; function YourComponent({ initialProp = 0 }) { const [state, setState] = useState(initialProp); return ( <div> {/* Your component JSX */} </div> ); } // Auto-mount when the script loads document.addEventListener('DOMContentLoaded', function() { const containers = document.querySelectorAll('[data-component="your-component"]'); containers.forEach(container => { const props = JSON.parse(container.getAttribute('data-props') || '{}'); const root = ReactDOM.createRoot(container); root.render(<YourComponent {...props} />); }); });Use the shortcode in your markdown:
{{< react-component name="counter" id="my-counter" props='{"initialCount": 10}' >}}
How the Shortcode Works Link to heading
The custom Hugo shortcode:
- Creates a container div with a unique ID for the React component
- Loads React and ReactDOM from a CDN (only once per page)
- Transpiles JSX to JavaScript using Hugo’s Babel pipe during build
- Loads the transpiled component as a regular JavaScript file
- Auto-mounts the component when the page loads
Benefits of This Approach Link to heading
- ✅ Proper Build-Time Transpilation - JSX is converted to JavaScript during Hugo build
- ✅ No Browser-Side Babel - Faster page loads without Babel Standalone (~2MB!)
- ✅ Progressive Enhancement - Static content works without JavaScript
- ✅ Selective Loading - Only loads React when components are used
- ✅ Easy to Use - Simple shortcode syntax
- ✅ Flexible - Can pass props as JSON
- ✅ Production Ready - Transpiled code is optimized and cached
Performance Considerations Link to heading
This approach offers excellent performance:
- ✅ JSX transpilation happens at build time, not in the browser
- ✅ React and ReactDOM are loaded from CDN (~130KB gzipped)
- ✅ Each component is transpiled and cached by Hugo
- ✅ No extra libraries needed in the browser
- ✅ Fast page loads and excellent SEO
Conclusion Link to heading
Combining Hugo’s speed with React’s interactivity gives you the best of both worlds. Your content remains fast and SEO-friendly while offering rich, interactive experiences where needed.
Try creating your own React components and embedding them in your Hugo posts!
Resources Link to heading
Happy coding! 🚀