Вход на сайт

Просмотр новости

Найдите то, что Вас интересует

Add a JSON API to Any Static Site Generator

Дата публикации: 23-04-2026 00:00:00

Back in 2015, I wrote about adding a JSON API to a Jekyll blog. The idea was simple: if your static site generator can output HTML from templates, it can output JSON from those same templates. No server-side code, no plugins, no runtime dependencies. Just another output format alongside your pages.
That technique is not specific to Jekyll. It works with any static site generator that has a templating system. This blog runs on Hugo now. Hugo has a built-in feature for exactly this kind of thing and it makes the whole process much cleaner. So I figured I would revisit the idea.
The idea, again
A static site generator takes your markdown and pushes it through templates to make HTML files. There is nothing special about HTML here –it is just the output format. If you write a template that outputs JSON instead of HTML, you get .json files. Your web server doesn’t care, it serves them the same way and JavaScript on the client side can fetch them.
Hugo’s output formats
Hugo has this thing called custom output formats. Instead of hacking JSON into a regular HTML template like I had to do with Jekyll, I defined a new output format and gave it its own templates. Hugo generates both the HTML and the JSON for you in the same build.
Config
First, tell Hugo about the JSON format. In hugo.toml:
[outputFormats.JSON]
mediaType = "application/json"
baseName = "index"
[outputs]
home = ["HTML", "RSS", "JSON"]
section = ["HTML", "JSON"]
page = ["HTML", "JSON"]
The outputs section is where you decide what gets a JSON version. If you only want a listing endpoint and don’t need individual post JSON, just leave page off.
The list template
Hugo will look for a template called list.json.json for sections and the homepage. Create it at layouts/_default/list.json.json:
[
{{ range $index, $page := .Pages }}
{{ if $index }},{{ end }}
{
"title": {{ .Title | jsonify }},
"url": {{ .RelPermalink | jsonify }},
"date": {{ .Date.Format "2006-01-02" | jsonify }},
"description": {{ .Params.description | default "" | jsonify }},
"tags": {{ .Params.tags | default slice | jsonify }}
}
{{ end }}
]
See that jsonify function? That is doing all the heavy lifting. It handles escaping, it handles arrays, it handles nil values. In the Jekyll version I had to loop through tags one at a time and build the JSON array by hand. Here I just pipe to jsonify and Hugo figures it out.
The single post template
For individual posts, create layouts/_default/single.json.json:
{
"title": {{ .Title | jsonify }},
"url": {{ .RelPermalink | jsonify }},
"date": {{ .Date.Format "2006-01-02" | jsonify }},
"description": {{ .Params.description | default "" | jsonify }},
"tags": {{ .Params.tags | default slice | jsonify }},
"content": {{ .Content | jsonify }}
}
Build it
Run hugo and check the output. For every page that has JSON in its output list, there will be an index.json sitting right next to the index.html:
/posts/index.json – all your posts
/posts/2026/04/23/some-post/index.json – one post
/index.json – homepage listing
It is simpler with Hugo. No Gulp task, no separate build step, no copying files around. But this same idea is possible in nearly any Static Site Generator.
The homepage template
My homepage template is different from my default list template. It uses .Site.RegularPages instead of .Pages and limits to 50 posts. If yours is the same, you might want a separate index.json.json in your layouts root:
[
{{ range $index, $page := first 50 .Site.RegularPages }}
{{ if $index }},{{ end }}
{
"title": {{ .Title | jsonify }},
"url": {{ .RelPermalink | jsonify }},
"date": {{ .Date.Format "2006-01-02" | jsonify }},
"description": {{ .Params.description | default "" | jsonify }},
"tags": {{ .Params.tags | default slice | jsonify }}
}
{{ end }}
]
What Hugo gives you for free
Comparing this to what I went through with Jekyll:
In Jekyll, I had to loop through tags and categories and manually comma-separate them with forloop.last checks. I had to add nil guards for when tags or categories were empty. I used Liquid’s escape filter on content and hoped for the best. And I had a whole Gulp task that duplicated the posts from _posts into an api/v01/posts directory and swapped the layout from html to json.
With Hugo, I write a template, add three lines to my config, and it just works. The jsonify function handles arrays, escaping, and nil values. The output format system handles the file generation. No Gulp task, no extra build step.
This isn’t just a Hugo thing
The core trick works anywhere. If your static site generator has templates, you can make it output JSON. Hugo happens to have first-class support for it, but the pattern is the same whether you are using 11ty, Astro, or even Jekyll like I was in 2015. Write a template, output JSON, let the build generate the files.
I use it for client-side filtering. Fetch the listing endpoint, match on tags in JavaScript, render a related posts section without a page reload. It is not a replacement for a real API –there is no auth, no pagination, no writes. But for read-only content that is already public on your site, it is free and it ships with every build.

Основное содержимое страницы с новостью.

April 23, 2026

tutorial hugo rest frontpage

Back in 2015, I wrote about adding a JSON API to a Jekyll blog. The idea was simple: if your static site generator can output HTML from templates, it can output JSON from those same templates. No server-side code, no plugins, no runtime dependencies. Just another output format alongside your pages.

That technique is not specific to Jekyll. It works with any static site generator that has a templating system. This blog runs on Hugo now. Hugo has a built-in feature for exactly this kind of thing and it makes the whole process much cleaner. So I figured I would revisit the idea.

The idea, again

A static site generator takes your markdown and pushes it through templates to make HTML files. There is nothing special about HTML here –it is just the output format. If you write a template that outputs JSON instead of HTML, you get .json files. Your web server doesn’t care, it serves them the same way and JavaScript on the client side can fetch them.

Hugo’s output formats

Hugo has this thing called custom output formats. Instead of hacking JSON into a regular HTML template like I had to do with Jekyll, I defined a new output format and gave it its own templates. Hugo generates both the HTML and the JSON for you in the same build.

Config

First, tell Hugo about the JSON format. In hugo.toml:

[outputFormats.JSON]
  mediaType = "application/json"
  baseName = "index"

[outputs]
  home = ["HTML", "RSS", "JSON"]
  section = ["HTML", "JSON"]
  page = ["HTML", "JSON"]

The outputs section is where you decide what gets a JSON version. If you only want a listing endpoint and don’t need individual post JSON, just leave page off.

The list template

Hugo will look for a template called list.json.json for sections and the homepage. Create it at layouts/_default/list.json.json:

[
{{ range $index, $page := .Pages }}
  {{ if $index }},{{ end }}
  {
    "title": {{ .Title | jsonify }},
    "url": {{ .RelPermalink | jsonify }},
    "date": {{ .Date.Format "2006-01-02" | jsonify }},
    "description": {{ .Params.description | default "" | jsonify }},
    "tags": {{ .Params.tags | default slice | jsonify }}
  }
{{ end }}
]

See that jsonify function? That is doing all the heavy lifting. It handles escaping, it handles arrays, it handles nil values. In the Jekyll version I had to loop through tags one at a time and build the JSON array by hand. Here I just pipe to jsonify and Hugo figures it out.

The single post template

For individual posts, create layouts/_default/single.json.json:

{
  "title": {{ .Title | jsonify }},
  "url": {{ .RelPermalink | jsonify }},
  "date": {{ .Date.Format "2006-01-02" | jsonify }},
  "description": {{ .Params.description | default "" | jsonify }},
  "tags": {{ .Params.tags | default slice | jsonify }},
  "content": {{ .Content | jsonify }}
}
Build it

Run hugo and check the output. For every page that has JSON in its output list, there will be an index.json sitting right next to the index.html:

  • /posts/index.json – all your posts
  • /posts/2026/04/23/some-post/index.json – one post
  • /index.json – homepage listing

It is simpler with Hugo. No Gulp task, no separate build step, no copying files around. But this same idea is possible in nearly any Static Site Generator.

The homepage template

My homepage template is different from my default list template. It uses .Site.RegularPages instead of .Pages and limits to 50 posts. If yours is the same, you might want a separate index.json.json in your layouts root:

[
{{ range $index, $page := first 50 .Site.RegularPages }}
  {{ if $index }},{{ end }}
  {
    "title": {{ .Title | jsonify }},
    "url": {{ .RelPermalink | jsonify }},
    "date": {{ .Date.Format "2006-01-02" | jsonify }},
    "description": {{ .Params.description | default "" | jsonify }},
    "tags": {{ .Params.tags | default slice | jsonify }}
  }
{{ end }}
]
What Hugo gives you for free

Comparing this to what I went through with Jekyll:

In Jekyll, I had to loop through tags and categories and manually comma-separate them with forloop.last checks. I had to add nil guards for when tags or categories were empty. I used Liquid’s escape filter on content and hoped for the best. And I had a whole Gulp task that duplicated the posts from _posts into an api/v01/posts directory and swapped the layout from html to json.

With Hugo, I write a template, add three lines to my config, and it just works. The jsonify function handles arrays, escaping, and nil values. The output format system handles the file generation. No Gulp task, no extra build step.

This isn’t just a Hugo thing

The core trick works anywhere. If your static site generator has templates, you can make it output JSON. Hugo happens to have first-class support for it, but the pattern is the same whether you are using 11ty, Astro, or even Jekyll like I was in 2015. Write a template, output JSON, let the build generate the files.

I use it for client-side filtering. Fetch the listing endpoint, match on tags in JavaScript, render a related posts section without a page reload. It is not a replacement for a real API –there is no auth, no pagination, no writes. But for read-only content that is already public on your site, it is free and it ships with every build.

Схожие новости

#Наименование новостиТональностьИнформативностьДата публикации
1EIP Editing Office Hour (EIP + ERC ) Meeting #107, July 21, 2026017.6215-07-2026
2Daily Hacker News for 2026-07-28011.4229-07-2026
3django-freeze: Convert Django Sites to Static Ones024.2923-04-2026
4Daily Hacker News for 2026-07-20010.7321-07-2026
5djangofmt - A fast, HTML aware, Django template formatter, written in Rust01021-02-2026
6the hauntings of Andrew Wyeth0213-07-2026
7Across all universes, Tofu will find him0002-07-2026
8Putting together some asks about my Project Hail Mary Atlantis AU. Thank you!! :D I’m glad y'all are…0205-07-2026
9django-nanopages - Django pages from Markdown026.6725-01-2026
10Релиз языка программирования Mojo 1.009.9111-08-2026

Классификация: . Схожих патентов: 0. Схожих новостей: 10. Тональность: 0. Информативность: 6.83. Источник: www.frobiovox.com.