It's finally here: our website has received a major update. After running on Hexo for years, it was time for something new. We chose Eleventy (11ty) as our static site generator, combined with Tailwind CSS for styling.
Why the switch?
Our old Hexo website served us well, but there were a few points that bothered us:
- Outdated dependencies - Node packages that were no longer maintained
- Limited flexibility - Layout adjustments were cumbersome
- Build performance - The build became increasingly slow as the site grew
The pain points in detail
The biggest problem was maintenance burden. Every time we ran npm install, we got a long list of security warnings. Packages that hadn't been updated for years, dependencies of dependencies causing conflicts.
"The best code is code you don't have to maintain."
This wisdom resonated more and more as we spent more time updating tooling than writing content.
What we were looking for
We compiled a list of requirements:
- Modern tooling with active community
- Simple architecture without too much abstraction
- Good developer experience with fast builds
- Flexibility in templating and styling
The technical stack
After evaluating various options, we settled on this combination:
| Component | Choice | Why |
|---|---|---|
| SSG | Eleventy 3.x | Simplicity, speed, flexibility |
| Styling | Tailwind CSS 4 | Utility-first, no CSS bloat |
| Templating | Nunjucks | Powerful, good 11ty integration |
| Hosting | Netlify | Automatic deploys, CDN |
Eleventy: the core
11ty is refreshingly simple. No magical configuration, no hidden conventions. You give it files, it makes HTML from them.
Configuration
The entire configuration fits in one file:
export default function (eleventyConfig) {
// Collections for blog posts
eleventyConfig.addCollection("blogNL", function(api) {
return api.getFilteredByTag("blogNL")
.sort((a, b) => b.date - a.date);
});
// Passthrough for images
eleventyConfig.addPassthroughCopy('src/assets/img');
return {
dir: { input: 'src', output: 'dist' }
};
}
Filters
Custom filters make life easier:
// Date formatting per language
eleventyConfig.addFilter("localDate", function(date, lang) {
const locale = lang === 'nl' ? 'nl-NL' : 'en-US';
return new Date(date).toLocaleDateString(locale, {
year: 'numeric',
month: 'long',
day: 'numeric'
});
});
Tailwind CSS 4
The latest version of Tailwind brings significant improvements:
New features
- CSS-first configuration - No more
tailwind.config.jsneeded - Native CSS nesting - Cleaner syntax
- Improved performance - Faster builds
Example: a button component
<button class="
bg-blue-600 hover:bg-blue-700
text-white font-semibold
px-6 py-3 rounded-lg
transition-colors duration-200
shadow-lg hover:shadow-xl
">
Submit
</button>
The result: consistent design without growing CSS files.
Content management
Directory structure
We chose an intuitive folder structure:
src/
├── blog/
│ ├── blog.json # Defaults for NL posts
│ ├── 2026-01-13-post/
│ │ ├── index.md # Content
│ │ └── banner.jpg # Images
│ └── legacy/
│ └── old-post/
│ └── index.md
├── en/
│ └── blog/
│ ├── blog.json # Defaults for EN posts
│ └── 2026-01-13-post/
│ └── index.md
└── _includes/
├── post.njk
└── blog-card.njk
Co-located images
A handy feature: images are stored in the same folder as the blog post.
Advantages
- Overview - Everything together
- Deletion - Delete the folder, done
- Moving - Drag & drop the entire folder
Implementation
In the frontmatter:
banner: banner.jpg # Relative path
In the template:
<img src="[page.url][banner]" alt="[title]">
(With Nunjucks variables between double curly braces)
Bilingual setup
URL structure
| Language | Blog index | Post URL |
|---|---|---|
| Dutch | /blog/ |
/blog/2026-01-13-post/ |
| English | /en/blog/ |
/en/blog/2026-01-13-post/ |
Linking translations
Via the translationKey in frontmatter:
# Dutch version
translationKey: my-post-2026
lang: nl
# English version
translationKey: my-post-2026
lang: en
The template automatically shows a link to the translation when available.
What we learned
Do's
- Start simple - Add features when needed
- Preserve URLs - Important for SEO and bookmarks
- Document choices - Your future self will thank you
Don'ts
- Don't over-engineer - YAGNI (You Aren't Gonna Need It)
- Don't copy boilerplate - Understand what you're adding
- Don't forget accessibility - Semantic HTML, alt texts
Technical learnings
Performance optimization
- Lazy loading for images below the fold
- CSS purging for minimal bundle size
- Setting cache headers correctly
SEO considerations
- Canonical URLs for translations
- Structured data for blog posts
- Automatically generate sitemap
Next steps
The foundation is in place, but there's always more to do:
Short term
- [ ] Implement RSS feed
- [ ] Open Graph meta tags
- [ ] Style 404 page
Long term
- [ ] Search functionality
- [ ] Related posts
- [ ] Reading time indicator
Conclusion
The migration from Hexo to 11ty was worth it. We now have a faster, more maintainable website with modern tooling. The learning curve was minimal and the developer experience is excellent.
Have questions about our approach or want to discuss your own website? Get in touch!