SkillAgentSearch skills...

Fraglates

An open source templating engine for generating server-side hypertext templates and fragments.

Install / Use

/learn @getampt/Fraglates
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<div> <img src="https://raw.githubusercontent.com/getampt/fraglates/main/images/fraglates-logo.png" width="400" height="auto" alt="Fraglates"/> </div>

An open source templating engine built on top of Nunjucks for server-side rendering (SSR) hypertext templates and fragments. Includes support for precompiling, automatic caching, asynchronous filters and tags, and progressive rendering of templates with Suspense-like fallbacks using a single http request.


npm npm

These docs are a work in progress. Not all features are documented and are subject to change.

Installation and Usage

npm install fraglates

Import into your app, initialize with your template and/or precompiled template directory, and use the render method to render full templates or partial fragments.

import Fraglates from "fraglates";

// Create a new instance of Fraglates
const fraglates = new Fraglates({
  templates: "./templates", // templates directory
  precompiled: "./precompiled", // precompile template directory (optional)
});

// Render the whole template (template, context)
const fullpage = await fraglates.render("my-template.html", {
  title: "My Dynamic Title",
  items: ["one", "two", "three"],
});

// Render just the #header fragment (template#fragment, context)
const header = await fraglates.render("my-template.html#header", {
  title: "My Dynamic Title",
});

Note: Fraglates uses a caching mechanism on the returned instance. DO NOT destructure the render method or the caching will break (i.e. const { render } = new Fraglates(...);).

Progressive Rendering with ReadableStream

Fraglates supports progressive rendering by generating a ReadableStream that can be streamed to the browser. The stream() method takes three arguments:

  • template: The template to render.
  • context: The data/variables passed to the template.
  • blocks: An object with block names for keys and asynchronous callbacks for values.

The blocks object references any {% block someBlock %} ... {% endblock %} blocks in the supplied template. These blocks will be rendered based on your template definition and streamed as soon as the initial template is rendered. Their output will be progressively replaced by the result of the async callback functions as they are resolved and added to the stream.

// Render the whole template
const stream = await fraglates.stream(
  "my-template.html", // Template name
  { title: "My Dynamic Title" }, // Context/variables
  {
    // Blocks
    header: async () => {
      // do some async stuff
      return { headerText: "Resolved header" }; // return an object
    },
    someBlock: async () => {
      // do some async stuff
      return "This is a string"; // or return a string
    },
  }
);

If the async callback function returns an Object, Fraglates will render the same fragment with the object merged into the main context.

If the async callback returns a string, the block will be replaced by the string. You can use this to optional render different fragments to replace a block.

Note that when blocks/fragments are rendered in the main template, they will contain an added __fallback variable set to a boolean value of true. This can be used to conditionally render content within the blocks. The __fallback value is scoped to the block, so any nested blocks will not inherit this value.

Streaming to the browser

The stream() method returns a ReadableStream that can be sent to the browser using any modern framework. Here is an example using Hono:

import { Hono } from "hono";
const app = new Hono();

app.get("/stream-fraglates", (c) => {
  // Render the whole template
  const stream = await fraglates.stream(
    "my-template.html", // Template name
    { title: "My Dynamic Title" }, // Context/variables
    {
      // Blocks
      header: async () => {
        // do some async stuff
        return { headerText: "Resolved header" }; // return an object
      },
      someBlock: async () => {
        // do some async stuff
        return "This is a string"; // or return a string
      },
    }
  );

  return c.body(stream, {
    headers: {
      "Content-Type": "text/html; charset=UTF-8",
      "Transfer-Encoding": "chunked",
    },
  });
});

app.fire();

Templating

Fraglates uses Nunjucks as the core templating engine. Currently Nunjucks Environment methods such as addFilter() and addGlobal() are supported.

The templating syntax is the same as Nunjucks, with one important addition: content wrapped in {% block blockName %} and {% endblock %} tags are accessible as fragments. Fragments can be rendered independently, giving you the ability to collocate HTML code within the same template for better readability. See this HTMX essay on fragments for more information and motivation.

In the template below, you can either render the entire template, or just the header fragment using the render method.

<!-- my-template.html -->
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Simple Template</title>
  </head>

  <body>
    {% block header -%}
    <header>{{ headerText }}</header>
    {%- endblock %}

    <div>{{ content }}</div>
  </body>
</html>

Note that blocks can be nested and include any Nunjucks templating logic like conditionals, filters, includes, etc.

Fraglates supports advanced Nunjucks features such as template inheritance, includes, macros/imports, custom filters, and more.

Precompiling Templates

Fraglates provides the fraglates cli command to precompile templates into JavaScript files that can be lazy loaded with dynamic imports. This is significantly faster than reading directly from the file system.

From your project directory, run the following:

fraglates '**/*.{html,njk}' -p path/to/templates -o path/to/precompiled

This will compile all html and njk files in your path/to/templates directory and write them to the path/to/compiled directory as .js files. You should add this to your npm build script so that templates are compiled at build time as well.

If you want to compile templates while developing, you can add the --watch or -w flag to the above command to watch the template files for changes and automatically recompile.

Note: Compiled templates are referenced using the name of the template, e.g. my-template.html. If the precompiled template doesn't exist, Fraglates will fall back to the filesystem if you provide a templates directory on initialization.

Asynchronous Support

IMPORTANT: Fraglates is asynchronous by default because of how it lazy loads compiled templates with dynamic imports. Calls to the render function must be awaited.

Custom filters are automatically converted to asynchronous filters. They can return a synchronous result, a resolved promise, or a promise. See Add a custom filter for more information.

Any additional context/variables passed to the Fraglates render function must be resolved first.

const foo = await someAsyncCall();

await fraglates.render("my-template.html", {
  foo: foo, // foo is already resolved
  // or you can just await the async call here
  bar: await someAsyncFunction(),
});

Extending with Nunjucks

An instance of Fraglates creates a private Nunjucks Environment behind the scenes. This is to ensure any manipulate of the underlying environment would not compromise the Fraglates instance.

Fraglates does supports Nunjucks methods such as addFilter() and addGlobal(). Using these methods will affect all templates and fragments.

Add a custom filter

// Create a new instance of Fraglates
const fraglates = new Fraglates({
  templates: "./templates", // template directory
});

// Add an 'upper' filter
fraglates.addFilter("upper", (str) => str.toUpperCase());

// Add an async filter
fraglates.addFilter("getUser", async (id) => {
  let user = await data.get(id);
  return user;
});

Add a custom global

// Create a new instance of Fraglates
const fraglates = new Fraglates({
  templates: "./templates", // template directory
});

// Add global variable
fraglates.addGlobal("someGlobalVar", "This is Global");

// Add a global function
fraglates.addGlobal("rand", (x, y) => {
  return Math.floor(Math.random() * (y - x + 1) + x);
});

NOTE: Globals cannot be asynchronous. Defining async globals will throw an error.

Custom Tags

You can extend Fraglates even more by using custom tags. Custom tags are heavily inspired by Eleventy's Paired Shortcodes that allow you to create new "block" types within your templates.

Custom tags can be made asynchronous by passing an async function as the second parameter. The function signature is as follows:

  • content: Anything wrapped inside the custom tag block
  • keywords: Auto-parsed keywords using Nunjucks' [keyword arguments](https://mozilla.github.io/nunjucks/templating.html#ke
View on GitHub
GitHub Stars23
CategoryDevelopment
Updated5d ago
Forks0

Languages

TypeScript

Security Score

75/100

Audited on Mar 25, 2026

No findings