You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Eleventy中使用EJS实现组件组合(含嵌套include)?

Simulating Slot/Children Composition in Eleventy + EJS

Great question! You’re absolutely on the right track leaning into partials for component composition—while EJS doesn’t have native support for React/Vue-style props.children or <slot> functionality, we can simulate this behavior effectively using Eleventy’s global helpers and EJS’s template rendering context.

Why Your Initial Approach Doesn’t Work Out of the Box

EJS’s include syntax doesn’t support passing callback functions as parameters (like the () => { /* markup */ } you tried), since include expects static data or variables, not executable code blocks. But we can work around this by capturing the output of your nested markup and passing that captured string as a children prop to your partials.

Solution 1: Capture Nested Markup with a Custom Helper

This is the closest to the syntax you’re aiming for. We’ll create a global helper function to capture the output of a template block, then pass that captured string to your partials.

Step 1: Register the Capture Helper in .eleventy.js

Add this to your Eleventy config to create a global capture function that taps into EJS’s output buffer:

module.exports = function(eleventyConfig) {
  eleventyConfig.addGlobal("capture", function(callback) {
    // Store the original EJS write method
    const originalWrite = this.ejs.write;
    let capturedOutput = "";

    // Replace the write method to capture output
    this.ejs.write = function(str) {
      capturedOutput += str;
    };

    // Execute the callback (your nested markup)
    callback();

    // Restore the original write method
    this.ejs.write = originalWrite;

    // Return the captured markup as a string
    return capturedOutput;
  });
};

Step 2: Use the Helper in Your Templates

Now you can write components exactly like you envisioned, using capture() to pass nested content as children:

Parent Template (e.g., page.ejs)

<%- include('../grid', { 
  tag: 'ul',
  // Capture the nested grid-item content and pass as children
  children: capture(() => { %>
    <%- include('../grid-item', { size: '50', tag: 'li' }, () => { %>
      <div>ANY MARKUP YOU WANT</div>
      <p>Dynamic content, variables, even other includes work here!</p>
    <% }) %>
    <%- include('../grid-item', { size: '50', tag: 'li' }, () => { %>
      <img src="/assets/example.jpg" alt="Custom image content">
    <% }) %>
  <% }) 
}) %>

grid.ejs Component

<<%= tag %> class="grid-container">
  <%= children %> <!-- Render the captured nested content -->
</<%= tag %>>

grid-item.ejs Component

<<%= tag %> class="grid-item size-<%= size %>">
  <%= children %> <!-- Render the captured content for this item -->
</<%= tag %>>

Solution 2: Custom Paired Shortcodes (Alternative)

If you prefer using Eleventy’s shortcode system instead of partials, you can create paired shortcodes that mimic slot behavior. This is similar to Nunjucks/Liquid’s paired shortcodes but tailored for EJS:

Step 1: Register Shortcodes in .eleventy.js

module.exports = function(eleventyConfig) {
  eleventyConfig.addPairedShortcode("Grid", function(content, options) {
    const { tag = 'ul' } = options;
    return `<${tag} class="grid-container">${content}</${tag}>`;
  });

  eleventyConfig.addPairedShortcode("GridItem", function(content, options) {
    const { size = '100', tag = 'li' } = options;
    return `<${tag} class="grid-item size-${size}">${content}</${tag}>`;
  });
};

Step 2: Use Shortcodes in EJS

In your template, call the shortcodes via the this context (Eleventy exposes shortcodes here for EJS):

<%= this.Grid(`
  ${this.GridItem('<div>ANY MARKUP</div>', { size: '50', tag: 'li' })}
  ${this.GridItem('<p>More custom content</p>', { size: '50', tag: 'li' })}
`, { tag: 'ul' }) %>

This works, but note that the nested content needs to be written as strings (or template literals), which is less intuitive than the first solution’s template-based syntax.

Key Notes

  • Keep using EJS’s strengths: Since you’re sticking with EJS, you can still leverage all its "pure JavaScript" features—variables, loops, conditionals, and data objects—right alongside this component pattern.
  • Avoid escaping issues: Use <%- %> instead of <%= %> when including partials or rendering captured content to ensure HTML isn’t escaped.
  • Test for whitespace: The capture helper will include any whitespace from your template, so you can minify output later if needed, or adjust your template formatting to avoid extra spaces.

Your initial intuition about using partials was correct—we just needed to add a small helper to bridge the gap between EJS’s include system and the slot/children pattern you’re used to.

内容的提问来源于stack exchange,提问作者Matt Stow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:03:27