____ _ ____ ____ ____ | _ \(_) ___ ___/ ___/ ___| / ___| | |_) | |/ __/ _ \___ \___ \| | _ | __/| | (_| (_) |__) |__) | |_| | |_| |_|\___\___/____/____/ \____|

Components in PicoSSG

PicoSSG lets you create reusable components for your site by leveraging Nunjucks's include and macro features. This guide shows how to create and use components to keep your templates DRY (Don't Repeat Yourself) and maintainable.

What Are Components?

In PicoSSG, components are reusable template fragments that you can include in multiple pages. They help you:

Basic Component Usage

The simplest way to create components is with Nunjucks's include directive. Put component files in a dedicated directory (like components/ or _includes/) with names starting with underscore to prevent them from being output as standalone pages.

Directory Structure

content/
├── components/
│   ├── _header.njk
│   ├── _footer.njk
│   └── _card.njk
├── index.html.md
└── about.html.md

Creating a Simple Component

Create a header component at content/components/_header.njk:


<header class="site-header">
  <div class="container">
    <a href="/" class="logo">My Site</a>
    <nav>
      <a href="/">Home</a>
      <a href="/about/">About</a>
      <a href="/blog/">Blog</a>
      <a href="/contact/">Contact</a>
    </nav>
  </div>
</header>

Including the Component

Use the include directive to add the component to your templates:

{% include "components/_header.njk" %}

<main>
  {{ content | safe }}
</main>

{% include "components/_footer.njk" %}

Passing Variables to Components

Basic components are often static, but you can pass variables for more flexibility:

In Your Layout Template

{% include "components/_header.njk" %}

<main class="container">
  <h1>{{ title }}</h1>
  {{ content | safe }}
</main>

{% include "components/_footer.njk" %}

In Your Component

<header class="site-header">
  <div class="container">
    <a href="/" class="logo">{{ site.title }}</a>
    <nav>
      <a href="/" class="{% if page.url == '/' %}active{% endif %}">Home</a>
      <a href="/about/" class="{% if page.url == '/about/' %}active{% endif %}">About</a>
      <a href="/blog/" class="{% if page.url == '/blog/' %}active{% endif %}">Blog</a>
      <a href="/contact/" class="{% if page.url == '/contact/' %}active{% endif %}">Contact</a>
    </nav>
  </div>
</header>

Creating Advanced Components with Macros

For more powerful components, use Nunjucks macros - they work like functions, accepting parameters and returning output.

Creating a Card Macro

Create content/components/_card.njk:

{% macro card(title, content, imageUrl='', link='', buttonText='Read More') %}
<div class="card">
  {% if imageUrl %}
    <img src="{{ imageUrl }}" alt="{{ title }}" class="card-image">
  {% endif %}
  
  <div class="card-body">
    <h3 class="card-title">{{ title }}</h3>
    <p class="card-content">{{ content }}</p>
    
    {% if link %}
      <a href="{{ link }}" class="card-button">{{ buttonText }}</a>
    {% endif %}
  </div>
</div>
{% endmacro %}

Using the Card Macro

Import and use the macro in your templates:

{% from "components/_card.njk" import card %}

<div class="card-grid">
  {{ card(
    title = "First Card",
    content = "This is the content for the first card.",
    imageUrl = "/images/card1.jpg",
    link = "/card1/"
  ) }}
  
  {{ card(
    title = "Second Card",
    content = "This is the content for the second card.",
    imageUrl = "/images/card2.jpg",
    link = "/card2/",
    buttonText = "Learn More"
  ) }}
  
  {{ card(
    title = "Third Card",
    content = "This is the content for the third card."
  ) }}
</div>

Component Libraries

For larger sites, create a component library by collecting related macros in a file:

Creating a Component Library

Create content/components/_ui-library.njk:

{# Button Component #}
{% macro button(text, url='#', type='primary', size='medium') %}
<a href="{{ url }}" class="btn btn-{{ type }} btn-{{ size }}">{{ text }}</a>
{% endmacro %}

{# Alert Component #}
{% macro alert(content, type='info', dismissible=false) %}
<div class="alert alert-{{ type }} {% if dismissible %}alert-dismissible{% endif %}">
  {{ content | safe }}
  {% if dismissible %}
    <button class="close">&times;</button>
  {% endif %}
</div>
{% endmacro %}

{# Icon Component #}
{% macro icon(name, size='medium') %}
<span class="icon icon-{{ name }} icon-{{ size }}"></span>
{% endmacro %}

Using the Component Library

Import and use the components:

{% from "components/_ui-library.njk" import button, alert, icon %}

<div class="container">
  {{ alert('Welcome to my site!', 'success', true) }}
  
  <h1>{{ title }}</h1>
  
  <div class="action-area">
    {{ button('Contact Us', '/contact/', 'primary', 'large') }}
    {{ button('Learn More', '/about/', 'secondary') }}
  </div>
  
  <footer>
    {{ icon('github') }} {{ icon('twitter') }} {{ icon('linkedin') }}
  </footer>
</div>

Components with Associated Assets

Components can include their own CSS and JavaScript. PicoSSG helps you organize this by preserving directory structure.

Component with Assets

content/
├── components/
│   ├── slider/
│   │   ├── _index.njk    (the component template)
│   │   ├── slider.css    (component-specific CSS)
│   │   └── slider.js     (component-specific JS)

Your slider component (_index.njk):

<link rel="stylesheet" href="/components/slider/slider.css">

<div class="slider" id="{{ id | default('slider-' + range(1000) | random) }}">
  <div class="slider-track">
    {% for slide in slides %}
      <div class="slide">{{ slide | safe }}</div>
    {% endfor %}
  </div>
  <button class="prev">Previous</button>
  <button class="next">Next</button>
</div>

<script src="/components/slider/slider.js"></script>

Using the Component with Assets

{% include "components/slider/_index.njk" with {
  id: 'homepage-slider',
  slides: [
    '<img src="/images/slide1.jpg" alt="Slide 1">',
    '<img src="/images/slide2.jpg" alt="Slide 2">',
    '<img src="/images/slide3.jpg" alt="Slide 3">'
  ]
} %}

Nested Components

Components can include other components to create more complex structures:

Layout Component

content/components/_layout.njk:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>{{ title }} | My Site</title>
  <link rel="stylesheet" href="/css/style.css">
</head>
<body>
  {% include "components/_header.njk" %}
  
  <main class="container">
    {{ content | safe }}
  </main>
  
  {% include "components/_footer.njk" %}
</body>
</html>

Best Practices

1. Use Naming Conventions

2. Make Components Reusable

3. Component Structure

4. Documentation

5. Performance

Troubleshooting

Component Not Found

If you get "Template Not Found" errors:

Variable Not Available

If variables aren't available in your component:

Related Topics