krz/orgo
Lightning fast org-mode static site generator.
clone: git clone https://gitbay.org/krz/orgo.git
v0.20.2: 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, at its default, commented
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.