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 single most important thing is measurement: local Lighthouse reported 47-98 on identical code on dops.no, and PSI scores are bimodal, so use the PSI API, run 12+ measurements per variant and compare fast-run fractions against a control - never a median of five. This article describes that workflow, the candidate fixes (defer heavy end-of-body 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), how to work safely on a live site, and four Docly-specific traps that generic web-perf guides never mention. Measured on dops.no: deferring end-of-body JS is the useful lever, decorative effects deferred to idle are effectively free, and removing 245 KB of third-party JavaScript is worth about one point - so decide those on UX grounds, not score grounds.
Work safely on a live site
On Docly the folder on disk is production - no git, no staging, no build step - so pointing an AI agent straight at a live site is extremely productive and instantly public. That trade is sensible on a low-traffic marketing site and reckless on a high-traffic, transactional or login-protected one. This article covers the practices that make it sustainable: back up before editing anything you did not create, know the blast radius (a new page is safe, a shared static asset reaches every visitor and outlives the fix via cache), never write JavaScript into production files via shell escaping, experiment on noindex throwaway pages rather than gating shared files, and verify with a cold cache - because your own browser serves the cached previous version and will show you a broken site working perfectly.