npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

eleventy-plugin-reusable-components

v1.0.1

Published

A configurable Eleventy plugin that enables a powerful component system for building dynamic, reusable HTML components across your static site.

Readme

Reusable Components for Eleventy Plugin

npm version Tests

A configurable Eleventy plugin that enables a powerful component system for building dynamic, reusable HTML components across your static site.

👉 Build your components once and use them anywhere.

Features

  • 🧩 Dynamic Component Rendering - Render components based on content data
  • 🎨 Template Language Agnostic - Works with Nunjucks, Liquid, WebC, Vento, and more
  • 🏗️ Flexible Configuration - Customizable directories and options
  • 🚀 Production Ready - Excludes development components from production builds
  • 🔧 Developer Friendly - Comprehensive error handling and debugging

Installation

npm install eleventy-plugin-reusable-components

Quick Start

1. Add the Plugin

Add the plugin to your Eleventy configuration file (.eleventy.js or eleventy.config.js).

import reusableComponents from "eleventy-plugin-reusable-components";

export default function(eleventyConfig) {
  eleventyConfig.addPlugin(reusableComponents);
}

2. Create a Component

Create a component file at src/components/callout.liquid:

---
title: Callout

# Default values
heading: Lorem ipsum dolor sit
description: A callout component to highlight important information.
links:
  - linkUrl: "#"
    linkText: Learn more
background: light
---
<div class="callout callout--{{ background }}">
  <h3 class="callout__heading">{{ heading }}</h3>
  <p class="callout__description">{{ description }}</p>
  {% if links %}
    <div class="callout__links">
      {% for link in links %}
        <a href="{{ link.linkUrl }}" class="callout__link">{{ link.linkText }}</a>
      {% endfor %}
    </div>
  {% endif %}
</div>
---
title: Callout

# Default values
heading: Lorem ipsum dolor sit
description: A callout component to highlight important information.
links:
  - linkUrl: "#"
    linkText: Learn more
background: light
---
<div class="callout callout--{{ background }}">
  <h3 class="callout__heading">{{ heading }}</h3>
  <p class="callout__description">{{ description }}</p>
  {% if links %}
    <div class="callout__links">
      {% for link in links %}
        <a href="{{ link.linkUrl }}" class="callout__link">{{ link.linkText }}</a>
      {% endfor %}
    </div>
  {% endif %}
</div>
---
title: Callout

# Default values
heading: Lorem ipsum dolor sit
description: A callout component to highlight important information.
links:
  - linkUrl: "#"
    linkText: Learn more
background: light
---
<div :class="`callout callout--${background}`">
  <h3 class="callout__heading" @text="heading"></h3>
  <p class="callout__description" @text="description"></p>
  <div class="callout__links" webc:if="links">
    <a 
      :href="link.linkUrl" 
      class="callout__link" 
      @text="link.linkText"
      webc:for="link of links">
    </a>
  </div>
</div>
---
title: Callout

# Default values
heading: Lorem ipsum dolor sit
description: A callout component to highlight important information.
links:
  - linkUrl: "#"
    linkText: Learn more
background: light
---
<div class="callout callout--{{ background }}">
  <h3 class="callout__heading">{{ heading }}</h3>
  <p class="callout__description">{{ description }}</p>
  {{ if links }}
    <div class="callout__links">
      {{ for link of links }}
        <a href="{{ link.linkUrl }}" class="callout__link">{{ link.linkText }}</a>
      {{ /for }}
    </div>
  {{ /if }}
</div>

3. Use the Component

In any template, use the renderComponent filter:

---
title: My Page

# Callout component data
callout:
  type: callout
  heading: Help organize the 11ty Meetup!
  description: A callout component to highlight important information.
  links:
    - linkUrl: https://11tymeetup.dev/
      linkText: Join the 11ty Meetup!
  background: warning
---

{{ callout | renderComponent }}
---
title: My Page

# Callout component data
callout:
  type: callout
  heading: Help organize the 11ty Meetup!
  description: A callout component to highlight important information.
  links:
    - linkUrl: https://11tymeetup.dev/
      linkText: Join the 11ty Meetup!
  background: warning
---

{{ callout | renderComponent | safe }}
---
title: My Page

# Callout component data
callout:
  type: callout
  heading: Help organize the 11ty Meetup!
  description: A callout component to highlight important information.
  links:
    - linkUrl: https://11tymeetup.dev/
      linkText: Join the 11ty Meetup!
  background: warning
---

<template @html="callout | renderComponent"></template>
---
title: My Page

# Callout component data
callout:
  type: callout
  heading: Help organize the 11ty Meetup!
  description: A callout component to highlight important information.
  links:
    - linkUrl: https://11tymeetup.dev/
      linkText: Join the 11ty Meetup!
  background: warning
---

{{ callout |> renderComponent |> safe }}

4. Render Multiple Components

You can render multiple components by passing an array of component data:

---
title: My Page

# Mixed component data
components:
  - type: callout
    heading: Help organize the 11ty Meetup!
    description: A callout component to highlight important information.
    links:
      - linkUrl: https://11tymeetup.dev/
        linkText: Join the 11ty Meetup!
    background: warning

  - type: text-and-image
    heading: About Our Community
    description: Join thousands of developers building amazing things with Eleventy.
    image: /assets/images/community.jpg
    imageAlt: Community members collaborating
    layout: image-right
---

{{ components | renderComponent }}
---
title: My Page

# Mixed component data
components:
  - type: callout
    heading: Help organize the 11ty Meetup!
    description: A callout component to highlight important information.
    links:
      - linkUrl: https://11tymeetup.dev/
        linkText: Join the 11ty Meetup!
    background: warning

  - type: text-and-image
    heading: About Our Community
    description: Join thousands of developers building amazing things with Eleventy.
    image: /assets/images/community.jpg
    imageAlt: Community members collaborating
    layout: image-right
---

{{ components | renderComponent | safe }}
---
title: My Page

# Mixed component data
components:
  - type: callout
    heading: Help organize the 11ty Meetup!
    description: A callout component to highlight important information.
    links:
      - linkUrl: https://11tymeetup.dev/
        linkText: Join the 11ty Meetup!
    background: warning

  - type: text-and-image
    heading: About Our Community
    description: Join thousands of developers building amazing things with Eleventy.
    image: /assets/images/community.jpg
    imageAlt: Community members collaborating
    layout: image-right
---

<template @html="components | renderComponent"></template>
---
title: My Page

# Mixed component data
components:
  - type: callout
    heading: Help organize the 11ty Meetup!
    description: A callout component to highlight important information.
    links:
      - linkUrl: https://11tymeetup.dev/
        linkText: Join the 11ty Meetup!
    background: warning

  - type: text-and-image
    heading: About Our Community
    description: Join thousands of developers building amazing things with Eleventy.
    image: /assets/images/community.jpg
    imageAlt: Community members collaborating
    layout: image-right
---

{{ components |> renderComponent |> safe }}

Note: The renderComponent filter accepts a template language parameter ("njk", "liquid", "webc", "vto", etc.) and can process both single components and arrays of components. The filter automatically merges component default values with your provided data - any missing fields will use the defaults from the component file.

Configuration

Default Options

const defaultOptions = {
  componentsDir: "src/components/*.*",
  collectionName: "components",
  enableRenderPlugin: true,
  excludeFromProduction: true
};

Usage Examples

Method 1: From Frontmatter

Define components directly in your page's frontmatter:

---
title: My Page
components:
  - type: callout
    heading: Welcome!
    description: Thanks for visiting our site
    background: primary

  - type: text-and-image
    heading: About Us
    description: Learn more about our company
    image: /assets/images/about.jpg
    imageAlt: About our company
---

<main>
  {{ components | renderComponent }}
</main>
---
title: My Page
components:
  - type: callout
    heading: Welcome!
    description: Thanks for visiting our site
    background: primary

  - type: text-and-image
    heading: About Us
    description: Learn more about our company
    image: /assets/images/about.jpg
    imageAlt: About our company
---

<main>
  {{ components | renderComponent | safe }}
</main>
---
title: My Page
components:
  - type: callout
    heading: Welcome!
    description: Thanks for visiting our site
    background: primary

  - type: text-and-image
    heading: About Us
    description: Learn more about our company
    image: /assets/images/about.jpg
    imageAlt: About our company
---

<main>
  <template @html="components | renderComponent"></template>
</main>
---
title: My Page
components:
  - type: callout
    heading: Welcome!
    description: Thanks for visiting our site
    background: primary

  - type: text-and-image
    heading: About Us
    description: Learn more about our company
    image: /assets/images/about.jpg
    imageAlt: About our company
---

<main>
  {{ components |> renderComponent |> safe }}
</main>

Method 2: Inline

Define components inline within your template:

{% assign heroComponent = {
  type: "hero",
  heading: "Welcome to Our Site",
  description: "Thanks for visiting! We're excited to share our content with you.",
  background: "primary"
} %}

{% assign features = [
  {
    type: "callout",
    heading: "Fast Performance",
    description: "Built for speed and efficiency.",
    background: "success"
  },
  {
    type: "callout", 
    heading: "Easy to Use",
    description: "Simple and intuitive interface.",
    background: "info"
  }
] %}

<main>
  {{ heroComponent | renderComponent }}
  
  <section class="features">
  {{ features | renderComponent }}
  </section>
</main>
{%- set heroComponent = {
  type: "hero",
  heading: "Welcome to Our Site",
  description: "Thanks for visiting! We're excited to share our content with you.",
  background: "primary"
} -%}

{%- set features = [
  {
    type: "callout",
    heading: "Fast Performance",
    description: "Built for speed and efficiency.",
    background: "success"
  },
  {
    type: "callout",
    heading: "Easy to Use", 
    description: "Simple and intuitive interface.",
    background: "info"
  }
] -%}

<main>
  {{ heroComponent | renderComponent | safe }}
  
  <section class="features">
  {{ features | renderComponent | safe }}
  </section>
</main>
<script webc:setup>
const heroComponent = {
  type: "hero",
  heading: "Welcome to Our Site",
  description: "Thanks for visiting! We're excited to share our content with you.",
  background: "primary"
};

const features = [
  {
    type: "callout",
    heading: "Fast Performance",
    description: "Built for speed and efficiency.",
    background: "success"
  },
  {
    type: "callout",
    heading: "Easy to Use",
    description: "Simple and intuitive interface.",
    background: "info"
  }
];
</script>

<main>
  <template @html="heroComponent | renderComponent"></template>
  
  <section class="features">
    <template @html="features | renderComponent"></template>
  </section>
</main>
{{ set heroComponent = {
  type: "hero",
  heading: "Welcome to Our Site",
  description: "Thanks for visiting! We're excited to share our content with you.",
  background: "primary"
} }}

{{ set features = [
  {
    type: "callout",
    heading: "Fast Performance",
    description: "Built for speed and efficiency.",
    background: "success"
  },
  {
    type: "callout",
    heading: "Easy to Use",
    description: "Simple and intuitive interface.",
    background: "info"
  }
] }}

<main>
  {{ heroComponent |> renderComponent |> safe }}
  
  <section class="features">
  {{ features |> renderComponent |> safe }}
  </section>
</main>

Method 3: From Data Files

Store component data in separate JSON files for better organization:

Data File: src/_data/homepage.json

{
  "hero": {
    "type": "hero",
    "heading": "Welcome to Our Site",
    "subheading": "Building amazing experiences",
    "image": "/assets/images/hero-bg.jpg",
    "ctaText": "Learn More",
    "ctaUrl": "/about/"
  },
  "sections": [
    {
      "type": "text-and-image",
      "heading": "Our Mission",
      "description": "We strive to create exceptional digital experiences that make a difference.",
      "image": "/assets/images/mission.jpg",
      "imageAlt": "Our mission in action",
      "layout": "image-right"
    },
    {
      "type": "callout",
      "heading": "Ready to Get Started?",
      "description": "Join thousands of satisfied customers today.",
      "background": "primary",
      "links": [
        {
          "linkText": "Sign Up Now",
          "linkUrl": "/signup/"
        },
        {
          "linkText": "Learn More",
          "linkUrl": "/features/"
        }
      ]
    },
    {
      "type": "stats-grid",
      "stats": [
        { "number": "10k+", "label": "Happy Customers" },
        { "number": "99.9%", "label": "Uptime" },
        { "number": "24/7", "label": "Support" },
        { "number": "50+", "label": "Countries" }
      ]
    }
  ]
}

Template Usage

---
title: Homepage
---

<main>
  {{ homepage.hero | renderComponent }}
  
  {{ homepage.sections | renderComponent }}
</main>
---
title: Homepage
---

<main>
  {{ homepage.hero | renderComponent | safe }}
  
  {{ homepage.sections | renderComponent | safe }}
</main>
---
title: Homepage
---

<main>
  <template @html="homepage.hero | renderComponent"></template>
  
  <template @html="homepage.sections | renderComponent"></template>
</main>
---
title: Homepage
---

<main>
  {{ homepage.hero |> renderComponent |> safe }}
  
  {{ homepage.sections |> renderComponent |> safe }}
</main>

Component Structure

Components should follow this structure:

---
title: ComponentName

# Default values
heading: "default heading"
description: "default description"
---

<!-- Component template here -->
<div class="component-name">
  <h2>{{ heading }}</h2>
  <p>{{ description }}</p>
</div>

Required Frontmatter

  • title: Used for component matching (gets slugified)

Component Matching

The plugin matches components by comparing:

  • Component's title (from frontmatter) → slugified
  • Content item's type property → slugified

Examples:

  • Component: title: "Text and Image" → slug: "text-and-image"
  • Content: type: "text-and-image"Match!
  • Component: title: "Callout" → slug: "callout"
  • Content: type: "callout"Match!

Default Values & Data Merging

Components automatically merge their default values with the data you provide. This means you only need to specify the fields you want to override - any missing fields will use the defaults from the component file.

Example:

If your component has these defaults:

---
title: Callout

# Default values
heading: "Default Heading"
description: "Default description"
background: "light"
links:
  - linkUrl: "#"
    linkText: "Default Link"
---

And you use it with partial data:

{% assign myCallout = {
  type: "callout",
  heading: "Custom Heading"
} %}

{{ myCallout | renderComponent: "liquid" }}

The component will render with:

  • heading: "Custom Heading" (from your data)
  • description: "Default description" (from component default)
  • background: "light" (from component default)
  • links: Default links array (from component default)

This ensures components always have complete data to work with, even when you only provide a subset of the required fields.

Error Handling

The plugin handles errors gracefully:

  • Missing component: Returns empty string
  • Invalid data: Returns empty string
  • Missing collections: Returns empty string
  • Template errors: Logged to console, returns fallback

Troubleshooting

Component Not Rendering

  1. Check component title matches type:
<!-- Component file -->
title: "My Component"  <!-- becomes "my-component" -->

<!-- Usage -->
type: "my-component"  <!-- must match! -->
  1. Verify component collection:
<!-- Debug: List all components -->
{% for component in collections.components %}
  <p>{{ component.data.title }} → {{ component.data.title | slugify }}</p>
{% endfor %}
  1. Check file location:
    • Default: src/components/*.njk
    • Custom: Set via componentsDir option

Template Language Issues

Make sure to specify the correct template language parameter for your template engine:

  • Liquid: {{ item | renderComponent }} (auto-escaped by default)
  • Nunjucks: {{ item | renderComponent | safe }}
  • WebC: <template @html="item | renderComponent"></template>
  • Vento: {{ item |> renderComponent |> safe }}

If no template language is specified, the filter will use the calling template's language by default.

API Reference

Plugin Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | componentsDir | string | "src/components/*.*" | Glob pattern for component files | | collectionName | string | "components" | Name of components collection | | enableRenderPlugin | boolean | true | Enable Eleventy Render Plugin | | excludeFromProduction | boolean | true | Exclude components from production |

Filters

renderComponent

Matches content items to component templates and renders them with automatic default value merging.

Parameters:

  • item (Object|Array): Content item(s) with type property
  • templateLang (string): Optional. Template language ("njk", "liquid", "webc", "vto", etc.). If not specified, defaults to the calling template's language.

Returns:

  • string: Fully rendered HTML content or empty string

Behavior:

The filter automatically merges component default values with your provided data. Any fields not specified in your data will use the default values from the component's frontmatter. This ensures components always have complete data to render properly.

Contributing

  1. Fork the repository
  2. Create a feature branch
  3. Make your changes
  4. Add tests
  5. Submit a pull request

License

MIT License - see LICENSE file for details.