TL;DR
- Put each snippet in the smallest scope that works, and keep an inventory of its owner, version and dependencies.
- Use dedicated data attributes as hooks, and make scripts tolerate pages where their elements are missing.
- Never place a private API key in browser code, and keep a removal path for every snippet.
A custom snippet should have a clear job. It might connect a form, initialise a slider or add behaviour the visual tools don’t cover. Trouble starts when nobody remembers where it was installed, what it depends on or whether another snippet already does the same thing.
Before adding code, I want three answers: which pages need it, which elements it controls and how we’ll know when it has stopped working.
Give each piece of code a home
Webflow has site-level code, page-level code and code embeds. Use the smallest scope that fits the requirement.
| Requirement | Likely location |
|---|---|
| Metadata or a stylesheet used throughout the site | Site head code |
| Behaviour present on every page | Shared script, loaded once |
| A feature on one landing page | Page-level code |
| Markup for one custom element | Embed near that element |
An external classic script with defer waits until the document has been parsed, and it keeps its order relative to other deferred classic scripts. async doesn’t guarantee that order. Use it when the script is independent, not just because it sounds faster.
An inline classic script doesn’t gain the same deferred behaviour by adding a defer attribute. Placement and initialisation still matter, and the MDN script element reference explains the details.
Select elements deliberately
Use a dedicated attribute when a script needs a stable target. Styling classes are likely to change during a redesign.
For example, a card can keep its normal Webflow classes and also carry a separate data-expand-card hook. That makes it clear which parts of the markup belong to the behaviour.
Also handle absence. A shared script shouldn’t throw an error because a particular page has no matching cards.
This small example toggles a details region. It assumes the HTML already includes a button with aria-controls pointing to the region’s ID:
document.querySelectorAll('[data-details-toggle]').forEach((button) => {
const panel = document.getElementById(button.getAttribute('aria-controls'));
if (!panel || button.dataset.initialised === 'true') return;
button.dataset.initialised = 'true';
button.setAttribute('aria-expanded', String(!panel.hidden));
button.addEventListener('click', () => {
panel.hidden = !panel.hidden;
button.setAttribute('aria-expanded', String(!panel.hidden));
});
});
Load it after those elements exist. A real feature also needs keyboard, styling and content checks. This only covers the toggle behaviour.
Stop two owners controlling one feature
If Webflow’s interactions and a custom script both change an element’s transform, the result depends on which one runs last. If a library is loaded globally and again inside a component embed, it may initialise twice.
Keep a small inventory with the feature, the script location, its dependency, its version and its owner. Before adding a slider or animation library, search the existing code and embeds for one already in place.
Never put a private API key in browser code. Hiding it in an embed or minifying the script doesn’t keep it secret. Requests that need private credentials belong in a controlled server-side service.
Test the published output
Designer and preview behaviour can differ from the published site, particularly around external resources and environment-dependent code. Check the custom-code options the project offers today, then test on staging.
Use the browser console and the Network panel. Look for failed files, duplicate requests, uncaught errors and scripts loading on pages that don’t need them.
Try the feature repeatedly. Open and close it, navigate away and back, resize the window and test with a keyboard. If the project uses client-side navigation, the initialisation needs a suitable lifecycle, rather than assuming every visit creates a new document.
Leave a removal path
Document what the snippet replaces and what happens if it’s removed. A decorative effect should leave usable content behind. A lead form needs a visible alternative if its external service fails.
Keep a copy of the previous code before editing. Where practical, publish one understood change at a time. When a problem appears, it’s far easier to investigate one adjustment than a batch of unrelated snippets. The performance side of third-party code is covered in A performance checklist for Webflow marketing sites, and the animation trade-offs are in Webflow Interactions vs custom code.
That’s the difference between custom code the next developer can maintain and custom code they’re afraid to touch.
