Images and attachments

attachmentswriting

An attachment is not a record in a media library. It is a file in your repository, sitting in a folder called _attachments, committed alongside the pages that reference it. Clone the repository and you have the images; delete the repository and they are gone with it. There is no separate asset store to back up and nothing to migrate.

Where they live

Each project keeps its own _attachments/ folder directly under the project directory:

handbook/
├── index.md
├── onboarding.md
├── policies/
│   ├── index.md
│   └── expenses.md
└── _attachments/
    ├── org-chart.png
    └── expense-policy.pdf

A folder named _attachments is skipped by the walker that builds the page tree — case insensitively, at any depth — so nothing inside it is ever listed or rendered as a page. Even a .md file in there is ignored. Repository layout covers the rest of the tree.

Adding an image in the editor

The toolbar has one upload control, an icon button labelled Upload image, in the same group as Insert link and Link to a document. It opens your operating system’s file picker, filtered to image files.

Pick a file and that is the whole interaction. There is no dialog, no preview step, and no confirmation: the file is uploaded into the project’s _attachments/ and the image appears at your cursor immediately. A failure raises a single alert reading Upload failed.

The upload writes the file to disk straight away, but the document is still unsaved — the image reference only reaches the .md file when you press Save.

What happens to the file name

The name is lowercased, and every character outside a-z, 0-9, ., _, and - becomes a hyphen, with runs of hyphens collapsed. Screen Shot 2026.PNG lands as screen-shot-2026.png.

Nothing is de-duplicated and nothing is ever overwritten. Upload the same picture twice and you get logo.png and logo-1.png — two identical files. A different file that happens to share a name gets the same numbered suffix. Renaming a file before you upload it is the only way to control what it ends up called.

The upload size ceiling is 200 MB per file by default. It is worth staying a long way under that, for reasons the Git section below explains.

What the editor does not do

  • No drag and drop. Dropping an image onto the page does nothing.
  • No paste. The editor takes plain text from the clipboard and discards the rest, so pasting a screenshot inserts nothing at all. Save it to a file first and use Upload image.
  • No non-image upload. The picker accepts images only. A PDF, a spreadsheet, or a ZIP gets into _attachments/ by being copied there with your file manager and committed like any other file.
  • No alt text prompt. The editor writes ![](…) with the description empty. Fill it in yourself in the source view — it is what a screen reader reads out, and it is indexed for search.

Referencing an attachment

The editor writes the reference as an absolute application path, which is stable wherever the document later moves to inside its project:

![Org chart](/projects/handbook/_attachments/org-chart.png)

Written by hand, a plain relative path works too, and is resolved against the folder the document sits in — so the number of ../ steps depends on how deep the page is:

![Org chart](_attachments/org-chart.png)          from handbook/onboarding.md
![Org chart](../_attachments/org-chart.png)       from handbook/policies/expenses.md

Both forms end up pointing at the same file. Either way the reference resolves within its own project: a path that climbs out into another project’s _attachments/ is left alone and does not resolve.

Non-image attachments use ordinary link syntax rather than image syntax:

The full text is in [the expense policy](_attachments/expense-policy.pdf).

Clicking that link on the server downloads or opens the file directly rather than trying to navigate to it as a page.

What exports do with them

Markdown export is literal — you get the .md file, references and all, with no files bundled alongside it.

Self-contained HTML and PDF export behave differently depending on how the attachment is referenced:

In the pageIn an HTML or PDF export
An image (.png, .jpg/.jpeg, .gif, .svg, .webp)Read from disk and embedded as a base64 data URI
Anything else, linked rather than embeddedLeft as a link — the file itself is not included

So an exported HTML file shows its pictures on a machine that has never seen your repository, but a link to a PDF in that same export points at a server the reader may not be able to reach. If an attachment has to travel with the document, put it in as an image. If it cannot be an image, send it separately.

Attachments and Git

Attachments are ordinary tracked files. Save a snapshot stages every new file under an _attachments/ folder along with new and changed .md files, so an image you uploaded this morning goes into the same commit as the paragraph you wrote about it. Nothing else new is swept in — the staging rules are in Connect your Git repository.

Three things follow from attachments being real files in real commits, and all three are worth knowing before a repository gets large.

Binaries grow history the way they do in any repository. Git stores a full new copy of a changed binary, and history is forever. A 12 MB uncompressed screenshot re-uploaded weekly costs 12 MB of permanent history each time. Export images at the size they are displayed at, and prefer PNG or WebP for screenshots; the discipline is the same one you would apply to a code repository.

Git LFS is not supported. Do not enable it on a DocuCommit repository. An LFS-tracked _attachments pattern would replace each image with a small text pointer file on push, and every other clone — including the server’s — would sync those pointers instead of the images. Pages would render broken images with no obvious cause. There is no LFS support to switch on, so the only safe configuration is not to use it.

A conflicting attachment has no merge view. The merge screen reads both sides as UTF-8 text, which is meaningless for an image. The path appears in the conflict list like any other, but you decide which file you want rather than merging them. See Resolve conflicts for how that screen behaves.

Housekeeping is yours

Nothing removes an attachment. There is no delete action, no unused-file report, and no cleanup pass: deleting a document leaves its images in _attachments/, and moving a document to another project copies its attachments across without removing the originals. The folder only ever grows.

That is not a serious problem at documentation scale, but it does mean the tidying is a manual job. Delete the files with your file manager or git rm, and commit the removal as its own snapshot.