Foundation: Setting Up 11ty with Modern Tooling
Part 1 of the "Building the Ultimate 11ty Vue Starter" series
Building a modern static site shouldn't feel like archaeology. While many tutorials focus on basic 11ty setups, we're going to create a foundation that's ready for Vue components, modern build tools, and future integrations with services like Shopify and email platforms.
In this first article, we'll establish a rock-solid 11ty foundation with modern tooling, optimal project structure, and developer experience that'll make you actually enjoy working on static sites.
What you'll have by the end: A blazing-fast 11ty site with hot reload, optimized development workflow, and a structure ready for Vue components and testing.
Why This Foundation Matters
Most 11ty tutorials give you a basic setup that works for simple blogs but falls apart when you need:
- Component-based architecture
- Modern build tools integration
- Comprehensive testing
- API integrations
- Team collaboration
We're building for the long term, creating a starter that scales from prototype to production.
Prerequisites
- Node.js 18+ installed
- Basic familiarity with JavaScript and npm
- A code editor (VS Code recommended for best experience)
- Terminal/command line comfort
Don't worry if you're new to 11ty - we'll explain everything along the way.
Step 1: Project Initialization
Let's start with a clean slate and build exactly what we need:
# Create project directory
mkdir my-11ty-starter
cd my-11ty-starter
# Initialize npm with sensible defaults
npm init -y
Now let's update our package.json
with project metadata and prepare for our tooling:
{
"name": "11ty-vue-starter",
"version": "1.0.0",
"description": "Modern 11ty starter with Vue 3, Vite, and comprehensive testing",
"main": ".eleventy.js",
"type": "module",
"scripts": {
"dev": "eleventy --serve --watch",
"build": "eleventy",
"clean": "rm -rf _site"
},
"keywords": ["11ty", "eleventy", "vue", "vite", "static-site"],
"author": "Your Name",
"license": "MIT",
"engines": {
"node": ">=18.0.0"
}
}
Key decisions:
"type": "module"
enables ES6 imports throughout our projectengines
field ensures Node.js compatibility- Scripts are simple now but will expand as we add tooling
Step 2: Install 11ty and Essential Dependencies
# Core 11ty
npm install --save-dev @11ty/eleventy
# Development dependencies we'll need
npm install --save-dev cross-env dotenv
# Create environment file for later
touch .env
Let's test our installation:
npm run build
You should see 11ty's output (even though we haven't created any files yet):
[11ty] Wrote 0 files in 0.02 seconds (v2.0.1)
Step 3: Modern Project Structure
This structure is designed for scalability and clear separation of concerns:
# Create our project structure
mkdir -p src/{_data,_includes/{layouts,components,partials},assets/{styles,scripts,images},posts}
mkdir -p public
touch .eleventy.js .gitignore README.md
Here's what each directory does:
my-11ty-starter/
├── src/ # All source files
│ ├── _data/ # Global data files (site config, APIs)
│ ├── _includes/ # Templates and reusable components
│ │ ├── layouts/ # Page layouts
│ │ ├── components/ # Reusable components (Vue later)
│ │ └── partials/ # Template partials
│ ├── assets/ # Source assets (will be processed)
│ │ ├── styles/ # CSS/SCSS files
│ │ ├── scripts/ # JavaScript files
│ │ └── images/ # Source images
│ ├── posts/ # Blog posts and content
│ ├── index.md # Homepage
│ ├── about.md # About page
│ └── blog.md # Blog index
├── public/ # Static assets (copied as-is)
├── _site/ # Generated site (ignored in git)
├── .eleventy.js # 11ty configuration
└── package.json
Why this structure?
src/
keeps all source files organized- Clear separation between processed (
assets/
) and static (public/
) files - Component structure ready for Vue integration
- Scalable for teams and large projects
Step 4: 11ty Configuration
Create .eleventy.js
with our foundation configuration:
import { EleventyHtmlBasePlugin } from "@11ty/eleventy";
export default function(eleventyConfig) {
// Copy static assets
eleventyConfig.addPassthroughCopy("src/assets/images");
eleventyConfig.addPassthroughCopy("public");
// Add official plugins
eleventyConfig.addPlugin(EleventyHtmlBasePlugin);
// Development server configuration
eleventyConfig.setServerOptions({
port: 8080,
showAllHosts: true,
// Enable hot reload for better DX
liveReload: true,
domDiff: true
});
// Watch for changes in assets
eleventyConfig.addWatchTarget("src/assets/");
// Custom date filter for blog posts
eleventyConfig.addFilter("dateFormat", (date) => {
return new Intl.DateTimeFormat('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(new Date(date));
});
// Create collections for blog posts
eleventyConfig.addCollection("posts", function(collectionApi) {
return collectionApi.getFilteredByTag("posts").sort((a, b) => {
return new Date(b.data.date) - new Date(a.data.date);
});
});
// Set custom directories
return {
dir: {
input: "src",
output: "_site",
includes: "_includes",
data: "_data"
},
// Use Nunjucks for templates (Vue will override for .vue files later)
markdownTemplateEngine: "njk",
htmlTemplateEngine: "njk",
dataTemplateEngine: "njk"
};
};
Configuration highlights:
EleventyHtmlBasePlugin
helps with URL resolution- Server options optimized for development experience
- Watch targets ensure hot reload works with our asset structure
- Custom date filter for better blog post formatting
- Template engine choice prepares for Vue integration
Step 5: Essential Files and Content
Let's create our basic site structure:
src/_data/site.js (Site metadata):
export default {
title: "11ty Vue Starter",
description: "A modern static site built with 11ty, Vue, and love",
url: process.env.URL || "http://localhost:8080",
author: {
name: "Your Name",
email: "[email protected]",
url: "https://your-site.com"
},
buildTime: new Date(),
environment: process.env.NODE_ENV || "development"
};
src/_includes/layouts/base.njk (Base HTML layout):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>
{%- if title -%}{{ title }} | {{ site.title }}{%- else -%}{{ site.title }}{%- endif -%}
</title>
<meta name="description" content="{{ description or site.description }}">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="{{ site.url }}{{ page.url }}">
<meta property="og:title" content="{{ title or site.title }}">
<meta property="og:description" content="{{ description or site.description }}">
<!-- Favicon placeholder -->
<link rel="icon" type="image/x-icon" href="/favicon.ico">
<!-- Styles will be added here in later articles -->
</head>
<body>
<header class="site-header">
<nav>
<a href="/" class="site-title">{{ site.title }}</a>
<ul class="nav-links">
<li><a href="/">Home</a></li>
<li><a href="/about/">About</a></li>
<li><a href="/blog/">Blog</a></li>
</ul>
</nav>
</header>
<main class="main-content">
{{ content | safe }}
</main>
<footer class="site-footer">
<p>© {{ site.buildTime | dateFormat }} {{ site.author.name }}. Built with 11ty.</p>
</footer>
</body>
</html>
src/index.md (Homepage):
---
layout: layouts/base.njk
title: Welcome to Your 11ty Site
description: A modern static site starter ready for Vue components and more
---
# Welcome to Your 11ty Starter
This is your homepage! This site is built with:
- **11ty** for static site generation
- **Modern tooling** for optimal developer experience
- **Scalable structure** ready for Vue components
- **Performance** and SEO optimized
## What's Next?
In the next articles, we'll add:
- Vue 3 components
- Vite for lightning-fast builds
- Tailwind CSS for styling
- Comprehensive testing
Check out the [about page](/about/) or browse the [blog](/blog/).
src/about.md (About page):
---
layout: layouts/base.njk
title: About This Site
description: Learn about this 11ty starter and how it's built
---
# About This Site
This is a modern 11ty starter designed for developers who want:
- **Modern tooling** without the complexity
- **Component-based** development
- **Testing** from day one
- **Performance** by default
Built by following the "Building the Ultimate 11ty Vue Starter" series.
## Tech Stack
- 11ty for static site generation
- Vue 3 (coming in Part 2)
- Vite (coming in Part 3)
- Tailwind CSS (coming in Part 4)
- Vitest for testing (coming in Part 5)
src/blog.md (Blog index page):
---
layout: layouts/base.njk
title: Blog
description: Latest posts and updates
---
# Blog
{% for post in collections.posts %}
<article>
<h2><a href="{{ post.url }}">{{ post.data.title }}</a></h2>
<time>{{ post.data.date | dateFormat }}</time>
<p>{{ post.data.description }}</p>
</article>
{% endfor %}
src/posts/hello-world.md (Sample blog post):
---
title: Hello, 11ty World!
description: Your first blog post in this modern 11ty starter
date: 2024-01-15
tags: ["posts"]
layout: layouts/base.njk
---
# Hello, 11ty World!
Welcome to your first blog post! This starter is ready for:
- Markdown content with frontmatter
- Tag-based organization
- SEO optimization
- Fast builds and hot reload
More posts coming soon as we build out this starter together.
Step 6: Basic Styling and Development Experience
Let's add minimal CSS to make our site presentable:
src/assets/styles/main.css:
/* Reset and base styles */
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
color: #333;
}
/* Layout */
.site-header {
background: #f8f9fa;
padding: 1rem 0;
border-bottom: 1px solid #e9ecef;
}
.site-header nav {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
display: flex;
justify-content: space-between;
align-items: center;
}
.site-title {
font-size: 1.5rem;
font-weight: bold;
text-decoration: none;
color: #333;
}
.nav-links {
display: flex;
list-style: none;
margin: 0;
padding: 0;
gap: 1rem;
}
.nav-links a {
text-decoration: none;
color: #666;
padding: 0.5rem;
border-radius: 4px;
transition: background-color 0.2s;
}
.nav-links a:hover {
background-color: #e9ecef;
color: #333;
}
.main-content {
max-width: 1200px;
margin: 0 auto;
padding: 2rem 1rem;
min-height: calc(100vh - 120px);
}
.site-footer {
background: #f8f9fa;
text-align: center;
padding: 1rem;
border-top: 1px solid #e9ecef;
color: #666;
font-size: 0.9rem;
}
/* Typography */
h1, h2, h3, h4, h5, h6 {
margin-top: 0;
margin-bottom: 1rem;
line-height: 1.3;
}
h1 { font-size: 2.5rem; }
h2 { font-size: 2rem; }
h3 { font-size: 1.5rem; }
p {
margin-bottom: 1rem;
}
/* Links */
a {
color: #0066cc;
}
a:hover {
color: #004499;
}
/* Code */
code {
background: #f8f9fa;
padding: 0.2rem 0.4rem;
border-radius: 3px;
font-size: 0.9em;
}
/* Responsive */
@media (max-width: 768px) {
.site-header nav {
flex-direction: column;
gap: 1rem;
}
.main-content {
padding: 1rem;
}
h1 { font-size: 2rem; }
h2 { font-size: 1.5rem; }
}
Update our base layout to include the CSS:
src/_includes/layouts/base.njk (add to <head>
):
<!-- Add this line in the head section -->
<link rel="stylesheet" href="/assets/styles/main.css">
Update .eleventy.js
to copy CSS:
// Add this line in the passthrough copies section
eleventyConfig.addPassthroughCopy("src/assets/styles");
Step 7: Environment Configuration
.gitignore
:
node_modules/
_site/
.env.local
.env
.DS_Store
*.log
dist/
coverage/
.nyc_output/
.env
(for development):
NODE_ENV=development
URL=http://localhost:8080
Testing Your Foundation
Let's verify everything works:
# Start the development server
npm run dev
You should see:
[11ty] Writing _site/index.html from ./src/index.md
[11ty] Writing _site/about/index.html from ./src/about.md
[11ty] Writing _site/blog/index.html from ./src/blog.md
[11ty] Writing _site/hello-world/index.html from ./src/posts/hello-world.md
[11ty] Wrote 4 files in 0.15 seconds (v2.0.1)
[11ty] Watching...
[11ty] Server at http://localhost:8080/
Visit http://localhost:8080
and you should see:
- A clean, responsive homepage
- Working navigation between pages
- A sample blog post
- Hot reload when you edit files
Test the hot reload: Edit src/pages/index.md
and save - your browser should automatically refresh!
What We've Built
🎉 Congratulations! You now have:
- Modern 11ty setup with ES6 modules and optimal configuration
- Scalable project structure ready for components and testing
- Hot reload development for immediate feedback
- SEO-optimized base layout with meta tags
- Blog functionality with markdown and frontmatter
- Responsive design that works on all devices
- Production-ready build process
Common Issues and Solutions
Problem: npm run dev
shows "command not found"
Solution: Make sure you're in the project directory and ran npm install
Problem: CSS not loading
Solution: Check that .eleventy.js
includes the passthrough copy for styles
Problem: Hot reload not working
Solution: Ensure liveReload: true
is set in .eleventy.js
server options
What's Next?
In Article 2, we'll supercharge this foundation by adding Vue 3 Single File Components. You'll learn how to:
- Integrate @11ty/eleventy-plugin-vue
- Convert our layouts to Vue components
- Use the Composition API in 11ty templates
- Pass data between 11ty and Vue seamlessly
Our basic site will transform into a modern component-based architecture while maintaining all the performance benefits of static generation.
Repository: The complete code for this article is available at github.com/yourname/11ty-vue-starter/tree/01-foundation
Live Demo: https://11ty-vue-starter-01.netlify.app
Part 2 coming next week: "Adding Vue 3 Single File Components to 11ty"