The data model
Understanding the Lume data model for pages
The content
When Lume loads a page, all its content is converted to a Data
object. This object may have the content
variable, with the file content. For example, the following markdown file:
**Content** of the page
is converted to this object:
{
content: "**Content** of the page",
}
If the file has additional variables, like a front matter, they are added to the Data
object:
---
title: Title of the page
---
**Content** of the page
Now the title is added to the page data:
{
title: "Title of the page",
content: "**Content** of the page",
}
This model is the same for any file that Lume can load. Even for images: let's say you have the imagick plugin to load and transform the data. The file content is loaded and stored in the content
variable:
{
content: Uint8Array(...)
}
If you use JavaScript modules for your pages, they are also translated to this model. For example:
export const title = "Title of the page";
export default function ({ title }) {
return `<h1>${title}</h1>`;
}
This is converted to:
{
title: "Title of the page",
content: function ({title}) {
return `<h1>${title}</h1>`;
}
}
Note that the default export is stored in the content
variable (the same as the markdown example above). Instead of exporting the function as default
, you could export it as content
, but not both:
// This works
export function content({ title }) {
return `<h1>${title}</h1>`;
}
// Mixing content and default won't work!!
export const content = "Content of the page";
export default function ({ title }) {
return `<h1>${title}</h1>`;
}
The content
variable does not always exist. Some data formats, like .yaml
or .json
may not export the content
variable. For example, the following page defined in a YAML file:
layout: main.vto
title: Page title
description: Page description
is converted to:
{
layout: "main.vto",
title: "Page title",
description: "Page description",
}
In this example, there's no content
variable and that is fine. The content
variable is only a convention used by formats that can export a nameless variable, like the default exports in ES modules or files with front matter and a content below.
Extra variables
Once the file is loaded and converted to the Data
model, Lume adds automatically 3 variables:
url
: Define the output URL of the page.date
: Define the date of the page.basename
: Define the basename of the page (only in Lume 2).
You can define the url
as a absolute or relative path or even a function (see URLs documentation for more info). Lume will resolve this value to a string, and if the url is false
, the page is ignored.
The date
variable can be defined in the filename (like 2023-11-30_hello-world.md
), using a page variable, etc. Lume will try to resolve it, using the file creation date as a fallback.
The basename
is like the filename. It can be used to change the last part of the URL.
For example, the following markdown file is saved in the posts/2023-11-30_hello-world.md
file:
Hello **world**
This is converted to:
{
url: "/posts/hello-world/",
date: Date("2023-11-30 00:00:00"),
basename: "hello-word",
content: "Hello **world**"
}
Data inheritance
Once a page is loaded, converted to Data
and the special variables url
, date
and basename
are assigned, it will be merged with other data defined in _data
files. Components stored in the _components
folders are added to the page under the comp
variable. Other variables defined by plugins like search
or paginate
are added too. So the markdown file:
Hello world
Is converted to:
{
url: "/posts/hello-world/",
date: Date("2023-11-30 00:00:00"),
basename: "hello-word",
content: "Hello **world**",
search: Searcher(),
paginate: Paginate(),
comp: {}
// etc...
}
Generators
If the content
variable is a Generator function, Lume will generate the pages at this point. More info about generating pages.
For example, let's say we have the following generator page:
export const layout = "main.vto";
export default function* () {
const numbers = [1, 2, 3];
for (const number of numbers) {
yield {
url: `/page-${number}/`,
content: `This is the page number ${number}`,
};
}
}
This will generate the three following pages:
{
layout: "main.vto",
url: "/page-1/",
content: "This is the page number 1",
date: Date(),
basename: "page-1",
search: Searcher(),
paginate: Paginate(),
comp: {}
}
{
layout: "main.vto",
url: "/page-2/",
content: "This is the page number 2",
date: Date(),
basename: "page-2",
search: Searcher(),
paginate: Paginate(),
comp: {}
}
{
layout: "main.vto",
url: "/page-3/",
content: "This is the page number 3",
date: Date(),
basename: "page-3",
search: Searcher(),
paginate: Paginate(),
comp: {}
}
Preprocessors
If you've defined a preprocessor in your _config
file, will be executed at this point. It allows modification of the Data
object before rendering. The preprocessors receive the Page
instance and you can access the data of the page with the Page.data
property.
// _config.js
site.preprocess([".html"], (page) => {
const data = page.data; // Get the Data object
data.title += " - My site name"; // Modify the title variable
});
Rendering
This is the process of rendering the Data
object (and Data.content
if it's defined) and saving the result in the page.content
variable. Anything defined in the Data
object is available in the template engine as a variable.
<!-- Render the title variable -->
<h1>{{ title }}</h1>
Processing
While Page.data
returns the Data
object of the page, Page.content
returns the result of rendering the data or, in other words, the content that will be output to the dest folder. You can use processors to modify this content:
site.process([".html"], (page) => {
// Get the rendered content
const content = page.content;
// Modify the content
page.content = content + "\n<!-- Created by Óscar Otero -->";
});
If the page content is HTML code, there's the document
property to use the DOM API to modify the content:
site.process([".html"], (page) => {
// Get the DOM
const document = page.document;
// Modify the content
document.querySelectorAll('a[href^="http"]').forEach((link) => {
link.setAttribute("target", "_blank");
});
});
Data conventions
In the Data
object you can store all variables that you want with the structure of your choice. But there are some special variables that Lume understands (See Standard variables documentation) and other variables considered good practices or common conventions. This is a list of all of them:
url string
: Created by Lume automatically if it's missing.
date Date
: Created by Lume automatically if it's missing.
basename string
: Created by Lume automatically if it's missing. Only Lume 2.
tags string[]
: Normalized by Lume automatically. Used to assign tags or to pages.
draft boolean
: If it's true
, the page will be ignored. Use LUME_DRAFT=true
to show draft pages.
renderOrder number
: To configure the rendering order of a page.
content string | Uint8Array | function | object
: The raw content of the page.
children string | Uint8Array | function | object
: The rendered content before being wrapped into layouts.
layout string
: The layout file used to render the page.
templateEngine string | string[]
: Configure different template engines to render the page.
mergedKeys Record<string, "array" | "stringArray" | "object">
: Configure how some data keys will be merged with the parent data.
onDemand boolean
: Whether render this page on demand or not.
lang string
: The language of the page.
type string
: The type of the page (post, article, etc). Used to group pages in different collections.
id string | number
: The id of the page. Used to identify a page inside a type.
comp object
: The components available for this page (from _components
folders).
page Page
: The current Page
instance.
alternates Data[]
: Other pages with the same content translated to other languages.
search Searcher
: A utility class to search pages and files.
paginate function
: A function to paginate the result of searching pages.