Gohyde reads _config.yml from the site root. Keys mirror Jekyll's, in
snake_case. A _config.local.yml, if present, is merged on top (last wins) for
local-only overrides that you don't commit.
title: My Site
description: A site built with Gohyde.
url: "https://example.com" # host + protocol — used by {{ site.url }}
baseurl: "/blog" # sub-path the site is served under
lang: en
source: . # default
destination: ./_site # output dir (some sites use ./_builds)
permalink: pretty # date | pretty | ordinal | none | custom pattern
markdown: goldmark # the only processor; kramdown sites work unchanged
url vs baseurlThese are distinct and both matter:
baseurl — the path prefix the site lives under (/blog). Prepended to pageurl — the absolute origin (https://example.com). Templates prepend it<link rel="canonical" href="{{ site.url }}{{ site.baseurl }}{{ page.url }}" />
<!-- → https://example.com/blog/about/ -->
If url is blank (e.g. commented out for local dev), {{ site.url }} renders
empty and links resolve relative — this is expected, not a bug.
Collections use list syntax (preferred):
collections:
- name: projects
label: Projects
output: true # write pages to the site (default true)
layout: project # default layout for items
blueprint: showcase # optional: share a field schema (see CMS docs)
src: _projects # source dir (default _<name>)
The old Jekyll map syntax is still accepted:
collections:
projects:
output: true
output: false collections are loaded (available as site.<name>) but not
written to the output directory.
Every collection is exposed two ways in Liquid:
{% for item in site.projects %}{{ item.title }}{% endfor %}
{% for collection in site.collections %}
{{ collection.label }}: {{ collection.docs | size }} docs
{% endfor %}
site.<name> (e.g. site.projects) is the array of that collection's docs.
site.collections is an array of {label, docs} — one entry per collection
(posts plus every custom collection), matching Jekyll's real shape (not a
bare {name: [...]} map).
The :path permalink token is relative to the collection's own directory and
excludes the source extension: _projects/alpha.md → alpha, not
_projects/alpha.md.
collections:
- name: projects
permalink: /work/:path/ # → /work/alpha/, not /work/alpha.md/
Jekyll-compatible jekyll-paginate (v1) behavior:
paginate: 5 # posts per page
paginate_path: /page:num/ # default; /blog/page:num/ paginates blog/index.html
The index.html in paginate_path's directory expands into one page per
chunk, each with a top-level paginator object:
{% for post in paginator.posts %}
<h2><a href="{{ post.url }}">{{ post.title }}</a></h2>
{% endfor %}
{% if paginator.previous_page %}
<a href="{{ paginator.previous_page_path }}">Newer</a>
{% endif %}
{% if paginator.next_page %}
<a href="{{ paginator.next_page_path }}">Older</a>
{% endif %}
<span>Page {{ paginator.page }} of {{ paginator.total_pages }}</span>
Page 1 is the index itself; page N lives at the paginate_path URL. As in
Jekyll, previous_page/next_page are nil on the first/last page, so plain
{% if %} guards work.
Per-page pagination: front matter (jekyll-paginate-v2 style) overrides the
globals — any page can opt in, wherever it lives:
---
title: Blog
permalink: /blog/
pagination:
enabled: true # false opts a page out of global pagination
per_page: 6 # overrides site `paginate:`
permalink: /blog/:num/ # overrides site `paginate_path:`
---
Posts with hidden: true front matter are excluded from pagination chunks and
the page count (they remain in site.posts).
Per-path front-matter defaults, exactly like Jekyll:
defaults:
- scope: { path: "", type: posts }
values: { layout: post, author: Vedad }
See Assets for the full pipeline. Key block:
assets:
digest: false # content-hash fingerprinting (true in production; always off under `gohyde serve`)
destination: assets # output subdir
sources: # dirs scanned for assets
- _assets
- _assets/css
- _assets/js
- assets
cdn:
url: "https://cdn.example.com" # when set, overrides baseurl on asset URLs
sass:
sass_dir: _sass
style: compressed # or expanded
source_maps: false
Gohyde uses Dart Sass when a genuine sass/dart-sass binary is on PATH. The
legacy Ruby sass gem is detected and skipped (it can't take --no-source-map);
if no real compiler is found, SCSS passes through unchanged with a stderr
warning — the build never hard-fails.
tailwind:
enabled: true
input: assets/css/input.css # default
output: assets/css/tailwind.css
minify: true # default; serve always compiles unminified
bundles: # optional: extra css compilations
- input: _assets/css/admin.css
output: assets/admin.min.css
Each bundles entry gets its own CLI run — and its own watcher under
gohyde serve; a failing bundle doesn't block the others.
Under gohyde serve, the Tailwind --watch subprocess starts only when
tailwind.enabled: true or the input file exists. Merely having the
tailwindcss CLI on PATH does not start it.
exclude:
- Gemfile
- node_modules
- README.md
- "*.psd"
JEKYLL_ENV is honored. The convention is to gate production behavior:
assets:
digest: false # flip to true on JEKYLL_ENV=production in your build script
JEKYLL_ENV=production gohyde build