krz/orgo

Lightning fast org-mode static site generator.

clone: git clone https://gitbay.org/krz/orgo.git

v0.21.0: docs/quickstart.org · raw

  1#+TITLE: Quick start
  2#+DESCRIPTION: A working site in two commands, then your own writing, then your own design.
  3#+LEDE: Five minutes from nothing to a site that reloads as you type.
  4
  5* Two commands
  6
  7#+BEGIN_SRC sh
  8orgo init my-site
  9orgo serve my-site -o _site
 10#+END_SRC
 11
 12Open [[http://127.0.0.1:3000][127.0.0.1:3000]]. Edit =my-site/index.org= in your editor, save, and the page reloads
 13on its own.
 14
 15=init= writes only files that do not already exist, so running it inside a directory that
 16already has content is safe and additive.
 17
 18** What init created
 19
 20#+BEGIN_EXAMPLE
 21my-site/
 22  orgo.toml            every setting, commented — all at their defaults but `theme`
 23  index.org               the home page
 24  blog/first-post.org     a post, to show the collection working
 25  templates/
 26    base.html             the page layout — edit this
 27    list.html             the blog index
 28    tags.html             the tag index
 29    feed.xml              an RSS feed
 30#+END_EXAMPLE
 31
 32* Adapting it to org files you already have
 33
 34You do not need =init=, a config file, or templates. Every command takes the same two
 35paths:
 36
 37#+BEGIN_SRC sh
 38orgo serve <SOURCE> -o <OUTPUT>
 39#+END_SRC
 40
 41** SOURCE is the URL root
 42
 43This is the one thing worth getting right, and it is not "the project directory" — it is
 44*the directory whose contents should sit at the top of your site*. A file at
 45=SOURCE/blog/post.org= is published at =/blog/post.html=.
 46
 47So if your writing lives in a =content/= subdirectory, point at =content/=, not at the
 48repository around it:
 49
 50#+BEGIN_SRC sh
 51cd ~/my-site
 52orgo serve content -o _site        # → /blog/post.html
 53#+END_SRC
 54
 55Pointing one level too high still builds, which is what makes it worth saying out loud.
 56It just builds the wrong site:
 57
 58#+BEGIN_SRC sh
 59orgo serve . -o _site              # → /content/blog/post.html
 60#+END_SRC
 61
 62Every URL gains a =/content/= prefix, and every non-org file in the repository —
 63=README.md=, build scripts, licence files — is copied into the output as a site asset.
 64If you see either symptom, you picked the directory above the one you meant.
 65
 66** Common layouts
 67
 68| Your files | Command |
 69|------------+---------|
 70| =~/notes/*.org= | =orgo serve ~/notes -o /tmp/notes-site= |
 71| =my-site/content/**/*.org= | =cd my-site && orgo serve content -o _site= |
 72| =my-site/*.org= at the top level | =cd my-site && orgo serve . -o _site= |
 73| Org files scattered in a code repo | Do not. Copy or symlink the ones you publish into one directory. |
 74
 75** OUTPUT can live inside the source
 76
 77=orgo serve . -o _site= is fine: the output directory is recognised and skipped, so
 78the build never copies its own output back into itself. Nothing dot-prefixed is published
 79either, so =.git= stays out of a site built from a repository root.
 80
 81** Where config and templates go
 82
 83Both live in the *source* directory — =SOURCE/orgo.toml= and =SOURCE/templates/= — and
 84neither is published. If you would rather keep the config elsewhere, name it:
 85
 86#+BEGIN_SRC sh
 87orgo serve content -o _site --config config/orgo.toml
 88#+END_SRC
 89
 90** A worked example
 91
 92A repository laid out as =content/= (org files), =theme/= (unrelated), =build.py=:
 93
 94#+BEGIN_SRC sh
 95cd ~/my-site
 96
 97# What is actually in there, before trusting anything with it.
 98orgo audit content
 99
100# Build it somewhere disposable and look.
101orgo serve content -o /tmp/preview
102
103# Happy with it? Build for real, failing on broken links.
104orgo build content -o _site --strict
105#+END_SRC
106
107The audit reports which org constructs appear, how often, and whether each is supported —
108names, counts and line numbers only, never your text. See
109[[file:guide/09-auditing.org][Auditing a corpus]].
110
111Nothing in your files has to change. With no config you get a complete site: a built-in
112layout, navigation across your top-level pages, and syntax highlighting.
113
114* Write a page
115
116Any =.org= file under the source directory becomes a page at the matching path.
117=notes/rust/borrowing.org= becomes =notes/rust/borrowing.html=.
118
119#+BEGIN_SRC org
120,#+TITLE: Borrowing
121,#+DATE: <2026-02-02 Mon>
122,#+FILETAGS: :rust:notes:
123,#+DESCRIPTION: How the borrow checker thinks about lifetimes.
124
125An opening paragraph, which becomes the excerpt in listings when there is no
126description.
127
128,* A heading
129
130Ordinary org: *bold*, /italic/, ~code~, [[https://orgmode.org][links]], and lists.
131
132,#+BEGIN_SRC rust
133fn main() {}
134,#+END_SRC
135#+END_SRC
136
137The keywords are all optional. =#+TITLE:= names the page, =#+DATE:= orders it in
138listings, =#+FILETAGS:= groups it on tag pages, and =#+DESCRIPTION:= is its summary.
139
140** Control the URL
141
142By default the filename decides the URL. =#+SLUG:= overrides it, which is how a
143date-prefixed filename becomes a clean address:
144
145#+BEGIN_SRC org
146,#+TITLE: Borrowing
147,#+SLUG: borrowing-explained
148#+END_SRC
149
150=2026-02-02-borrowing.org= now publishes as =borrowing-explained.html=.
151
152** Keep something unfinished out of the build
153
154#+BEGIN_SRC org
155,#+DRAFT: t
156#+END_SRC
157
158The page is not written, and does not appear in listings or navigation. Preview it while
159you work with =--drafts=:
160
161#+BEGIN_SRC sh
162orgo serve my-site -o _site --drafts
163#+END_SRC
164
165* Change the design
166
167Everything visual lives in =templates/base.html=. It is an ordinary
168[[https://docs.rs/minijinja][minijinja]] (Jinja2) template, and replacing it replaces the
169whole layout:
170
171#+BEGIN_SRC html
172<!DOCTYPE html>
173<html lang="{{ site.language }}">
174<head>
175  <meta charset="utf-8">
176  <title>{{ page.title }} — {{ site.title }}</title>
177  <link rel="stylesheet" href="{{ root }}style.css">
178</head>
179<body>
180  <nav>{% for item in nav %}<a href="{{ item.url }}">{{ item.title }}</a>{% endfor %}</nav>
181  <h1>{{ page.title }}</h1>
182  {{ body | safe }}
183</body>
184</html>
185#+END_SRC
186
187=root= is the =../= prefix back to the site root, so the same template works at any
188depth. Any other file in the source directory — =style.css=, images, fonts — is copied to
189the output untouched.
190
191Editing a template rebuilds every page that uses it, so the browser reloads while you are
192still looking at it. The full list of variables is in [[file:guide/04-templates.org][Templates]].
193
194* Add a blog index
195
196Listing pages have no source file; they are declared in =orgo.toml=:
197
198#+BEGIN_SRC toml
199[[collections]]
200source = "blog"
201output = "blog/index.html"
202template = "list.html"
203title = "Blog"
204sort = "date"
205order = "desc"
206nav = true
207#+END_SRC
208
209That is also how you get tag pages, pagination and an RSS feed — same mechanism, more
210settings. See [[file:guide/03-collections.org][Collections]].
211
212* Build for real
213
214#+BEGIN_SRC sh
215orgo build my-site -o _site --strict
216#+END_SRC
217
218=--strict= turns broken internal links and parse diagnostics into a non-zero exit, which
219is what you want in CI. Deployment is just copying =_site= somewhere; see
220[[file:guide/10-deploying.org][Deploying]].
221
222* Next
223
224- [[file:guide/01-cli.org][Command reference]] — every command and flag.
225- [[file:guide/02-configuration.org][Configuration]] — every setting in =orgo.toml=.
226- [[file:guide/05-org-support.org][Org support]] — exactly which org syntax is handled.