KB
Common pitfalls, recurring AI-agent misconceptions, API misuse and their resolutions. Each entry is an authoritative reference you can link to when the same issue comes up again.
Use tilde paths in JavaScript
Hardcoded leading-slash paths in JavaScript break the moment the app is published under a subfolder. On publish, Docly rewrites href and src attributes in static HTML — but not form action, and nothing JS emits at runtime. Use ~/ in all JS path strings and in any non-href/non-src HTML attribute that holds a path; it resolves to the current webapp's base path at runtime and works equally at the root.
Omit file extensions in links
Docly serves every page at a clean, extensionless URL and resolves the underlying file by name, so internal links must omit the .html and .hash suffix. A link that includes the extension still resolves — it is not broken — but it produces a non-canonical, noisier URL and splits SEO signals between two addresses for the same page.
Sort folder contents by a custom field
Make a folder's contents sortable by adding a CustomColumns entry to the folder's .docly properties: a Formula that reads a field from the contained documents, with Sorting set to "Sortable". Wrap numeric sort fields in parseInt() so they sort by value, not as text.
Give linked images SEO-friendly filenames
When you link an embedded image through Docly's scaling URL, the last path segment — the "virtual name" — becomes the image's public filename but is ignored when Docly resolves the file. Set it to a descriptive, keyword-bearing slug (commonly the page's own title via encodeURIComponent(Title)) so every generated image is served under an SEO-friendly name instead of a throwaway like image.jpg or photo-10.jpg.
Ship secure user access fast: let Docly handle login, you handle the permissions
Skip the authentication tax — let Docly's built-in login handle passwords, resets, sessions, 2FA and threat protection for you (enterprise-grade security in minutes, no auth code to write or maintain): invite users with docly.addFolderShare("/", email, "V", inviteMessage) and let Docly's login portal handle authentication, passwords, resets and sessions, while application rights (roles, per-customer access) live in a local user-registry folder with one document per email address, looked up via request.user in every API script.
Build client UI as structured web components
When a client-side Docly solution grows beyond a single page, structure the UI as native Web Components (custom elements) instead of one large script that manipulates the DOM by hand. Encapsulating markup, behavior and state inside reusable elements keeps the frontend maintainable, testable and portable across pages — and works without any framework or build step.
Group API endpoints into topic folders
Dumping every endpoint as a flat list of .js files directly in #/API/ becomes unreadable and collision-prone as an app grows. Docly maps any subfolder under #/API/ straight to the URL, so #/API/orders/create.js is reachable at /API/orders/create. Group related endpoints into topic folders (orders, users, billing) to get readable URLs, fewer naming clashes, and a structure that scales with the application.
Use master pages for shared layout
When several pages or display templates share the same layout — header, footer, navigation, common CSS/JS — define a single master page (e.g. #/master.hash) and have the other pages inherit from it with the MASTER directive. Repeating the full HTML chrome in every page leads to drift, inconsistent styling, and tedious multi-file edits.
Organize pages into topic folders
Dropping every .html and .hash file into a single folder produces a long, flat, unbrowsable list and flat, meaningless URLs as a site grows. Because Docly maps a file's path under the site straight to its URL, organising pages into topic folders (products, support, about) gives readable, RESTful URLs, clean section landing pages via Index files, and a structure that scales with the site instead of fighting it.
Keep index.hash out of content folders
index.hash is a fallback-resolved file: a single #/Folder/index.hash renders the listing for every folder, and #/Root/index.hash is served at the site root as the home page. Place index.hash files in the developer-only #/ tree instead of inside content folders — a copy next to the documents it renders means one template per folder, visible and editable to non-developer users, drifting out of sync with every layout change.
Separate code from content for non-developer users
When non-developer users will administer data in a Docly solution, move display assets (index.hash, shared CSS/JS, masters, partials) into #/Root and #/Folder. The #/ tree is invisible to anyone without developer access, so this keeps the user's workspace free of code files they shouldn't touch.
Use AVIF and WebP image formats
You keep uploading JPEG/PNG as before; Docly converts on serving, and the file extension on the display name in the image URL decides the output format (…/1200x800x0/produkt.webp ⇒ WebP). WebP has universal browser support, so <img src="….webp"> is enough on its own. AVIF must sit inside a <picture> with WebP/JPEG fallback, because Docly does not negotiate format via the Accept header — without a fallback, visitors without AVIF support see nothing. A safe site-wide default is images.defaultFormat: webp in #/site.json; avoid avif as a global default.
Restrict and register image sizes
The scaled image URL (/foto.jpg/1200x800x0/navn.jpg) is open by default: every unique size is a cache miss with heavy scaling and a new cache file on disk. A size policy locks scaling to a known set of sizes — and it is also what unlocks WebP/AVIF, which are refused on the URL path until the policy is set to auto or strict.
Cache-busting bundles with assetUrl
Browsers cache static files, so an unchanged bundle URL can serve stale JS/CSS for up to a day after a publish. Wrap your own <script src> and <link href> in assetUrl() to append a per-publish ?v= stamp that busts the cache. Never use assetUrl on dynamic or per-user output — ?v= URLs get max-age=1 year immutable, which would freeze the first dynamic response for everyone.
Optimize page performance
When a Docly page scores poorly on PageSpeed Insights (PSI) mobile, use Claude Code and the Claude Chrome plugin to diagnose which bottleneck actually applies before fixing anything — the cause varies per page and could be heavy end-of-body JavaScript, render-blocking Google Fonts, an oversized or late-painted LCP image, gtag.js, or decorative canvas effects. This article describes that diagnose-then-fix workflow, the candidate fixes to reach for (defer heavy JS to after load + idle, self-host the font, shrink/preload the LCP image, defer analytics and canvas effects, pin effect elements to avoid CLS), and four Docly-specific traps that generic web-perf guides never mention. On dops.no this workflow took mobile from 61 to 94-98 without removing the signature effects, but that is one worked example, not a fixed recipe.