Third-party Libraries and Scripts

This content was generated with the assistance of AI and should be verified against the official Webflow documentation before being relied on in production.

This sub-section’s Webflow bibliography lists the reference material consulted while preparing these pages.

This page was checked against the Webflow Help Center, the Webflow Developer documentation, the GSAP documentation and MDN on 2026-10-28. Webflow is a hosted service without version numbers, so a panel label or a limit can change between two visits. Custom code in the head and body of a site, and in the Code Embed element, is available on plans that allow custom code, and each of those code boxes accepts up to 50,000 characters according to the Webflow update that raised the limit. The libraries named below have versions of their own, and every version number on this page is an example to be replaced by the current release when you copy a snippet.

A Webflow site is built with a visual designer, but the browser that loads it sees ordinary HTML, CSS and JavaScript. Anything the designer cannot express can be added as a script or stylesheet from somewhere else: an animation library, a map, a payment form, an analytics tag. Each one is code written by another party, running with the full power of your page, delivered at the moment of each visit, and usually paid for in loading time and in the visitor’s privacy. This page is about adding such code deliberately. It explains how to load external CSS and JavaScript so a slow or compromised server cannot break the page, how to use GSAP plugins and attribute-based libraries, how to embed a map and a booking widget on the Fernbrook Gardens site (a fictitious botanical garden with an events collection and a ticket page), and how to review each addition for performance, privacy and security before it is published.

Where third-party code can live

Webflow offers four places for code, and the choice decides when the code runs and how widely it applies. The mechanics, plan requirements and the behaviour in the Designer are explained on Custom Code: Head, Footer and Embeds; this table only repeats what matters for third-party libraries.

Place Runs Suited to

Site settings, Custom code, Head code

On every page, while the head is parsed

Stylesheets, preconnect hints, defer scripts that must exist on every page

Site settings, Custom code, Footer code

On every page, after the body content

Scripts that act on the whole site, such as a consent manager or an attribute library

Page settings, Custom code (head and before the closing body tag)

On one page only

Libraries needed by one page, such as the booking widget on the ticket page

Code Embed element

At the position of the element, on its page (or in a component)

A map, a widget with its own container, a small stylesheet that only affects that block

The rule of thumb is to load a library as narrowly as it can work. A script that only the ticket page needs does not belong in the site footer, because every visitor of every page would then download it. The same applies to CSS: a stylesheet for a lightbox library loaded site-wide styles a lightbox that exists on one page.

Custom code does not run on the Designer canvas. It runs on the published site and in the preview that Webflow serves from a canvas.webflow.com address, so an embed that looks empty while designing is not necessarily broken. Test every library on the published staging address before you rely on it.

Loading external JavaScript safely

A script element in the middle of a page is, by default, a roadblock. The browser stops parsing the HTML, downloads the file, runs it, and only then continues. If the server hosting the file is slow, the page waits; if it is down, the page can appear blank for seconds. Three attributes change this behaviour, and a fourth pair protects against a file that has been replaced.

defer and async

  • defer downloads the file in parallel with parsing and runs it after the document has been parsed, in the order the scripts appear. It is the right default for libraries that work on the page’s elements.

  • async downloads in parallel and runs as soon as the file arrives, in whatever order downloads finish. It suits independent scripts that touch nothing else, such as an analytics tag.

  • A script with neither attribute blocks parsing. Reserve it for the rare case where later inline code needs the library immediately and cannot wait.

Both attributes apply only to scripts with a src; an inline script ignores them. Because a deferred script runs after parsing, code that needs the Webflow runtime should wait for it explicitly, as shown in Custom Attributes and JavaScript.

<!-- Site settings > Custom code > Head code -->
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin>
<script defer src="https://cdn.jsdelivr.net/npm/example-lib@1.2.3/dist/example-lib.min.js"></script>

Subresource Integrity

A pinned version number stops a library from changing under you only if the host honours it. Subresource Integrity (SRI) removes that trust: the page states the cryptographic hash of the file it expects, and the browser refuses to run a file that does not match. MDN describes the integrity attribute as a whitespace-separated list of an algorithm name (sha256, sha384 or sha512) and a base64 hash, and states that for a file served from another origin the crossorigin="anonymous" attribute is required, because without it the request is made in a mode in which SRI cannot be applied and the load fails.

<script
  defer
  src="https://cdn.example.com/libs/example-lib@1.2.3/example-lib.min.js"
  integrity="sha384-PLACEHOLDER_REPLACE_WITH_THE_REAL_BASE64_HASH"
  crossorigin="anonymous"></script>

<link
  rel="stylesheet"
  href="https://cdn.example.com/libs/example-lib@1.2.3/example-lib.min.css"
  integrity="sha384-PLACEHOLDER_REPLACE_WITH_THE_REAL_BASE64_HASH"
  crossorigin="anonymous">

The hash must be computed from the exact file that will be served. MDN gives two command-line recipes, one using OpenSSL and one using shasum; the OpenSSL one is shown here.

# Download the exact, version-pinned file, then hash it.
curl -sSfLo example-lib.min.js https://cdn.example.com/libs/example-lib@1.2.3/example-lib.min.js
cat example-lib.min.js | openssl dgst -sha384 -binary | openssl base64 -A
# Prefix the output with "sha384-" and paste it into the integrity attribute.

Two practical limits apply. SRI only works with a URL that always returns the same bytes, so it cannot be combined with an address that follows "latest" or a floating major version; the pinned, exact version is what makes the hash stable. And SRI protects the file, not the behaviour: the library you hash is still allowed to call other servers once it runs.

Pinning versions

Write the full version number in the address (1.2.3, not 1 or latest). A floating address changes the code on your live site without any publish on your side, which means a release of the library can break the Fernbrook ticket page at midnight with nothing in the Webflow activity log to explain it. A pinned address changes only when someone edits the custom code, and Webflow records that edit. Keep a short note of each pinned version in the page’s custom code as a comment, so a later reviewer can see what to compare against the library’s changelog.

Preconnect and fallbacks

A preconnect hint tells the browser to open the connection to a host (DNS, TCP and TLS) before the file is requested, which shortens the wait for a library on another origin. Use it for the one or two hosts that carry critical resources, and add crossorigin when the hint is for a script or a font, so the connection can be reused. Too many hints compete for the same early bandwidth and cost more than they save.

A fallback is code that keeps the page usable when the library does not load, because a network, an extension or a blocker removed it. Check that the global exists before using it, and make sure the content is readable without the script.

<script defer src="https://cdn.example.com/libs/example-lib@1.2.3/example-lib.min.js" integrity="sha384-PLACEHOLDER" crossorigin="anonymous"></script>
<script defer>
  window.addEventListener("DOMContentLoaded", function () {
    if (typeof window.ExampleLib === "undefined") {
      // The library did not load: leave the static content as it is.
      document.documentElement.classList.add("lib-unavailable");
      return;
    }
    window.ExampleLib.init({ selector: "[data-fernbrook-gallery]" });
  });
</script>

Because a deferred inline script is not deferred (the attribute applies only to files), the example above waits for DOMContentLoaded instead, which fires after all deferred scripts have run. The lib-unavailable class lets a style rule show the plain version of a block whose enhanced version needs the library.

Self-hosting versus a CDN

A library can be served from a public content delivery network (CDN) or from a file you control. Each has a cost.

Public CDN Self-hosted file

How it is referenced

A full URL in a script or link element

A file uploaded to Webflow’s asset storage (or your own origin) and referenced by its address

Speed

Often fast and close to the visitor, with a separate connection to open

One less origin to connect to

Integrity

You must add SRI to detect a changed file

The file only changes when you replace it, which is a form of pinning

Availability

Depends on a third party being up

Depends on Webflow’s hosting, which already serves the page

Privacy

The CDN operator receives each visitor’s address and the page that asked

No extra recipient

Maintenance

Change the version in the address

Download, review and re-upload each update

Webflow’s asset manager is designed for images, documents and similar media, and files of other types are not always accepted there. If your plan and the asset panel allow a JavaScript or CSS file, the upload gives you an address you can reference. When it does not, there are two other self-hosting routes: paste the library source into a Code Embed or the custom code boxes, subject to the 50,000-character limit per box, or serve the files from your own origin (for example through the arrangements in Reverse Proxy and Custom Hosting or an app on Webflow Cloud). Pasting minified code in the page also means it is not cached across pages, so use it for small files only.

For GSAP, Webflow takes the choice away: its site settings include a GSAP integration that loads the core and the plugins, so you do not need to choose a CDN, as the next section shows.

GSAP plugins in custom code

GSAP is the animation engine behind Interactions with GSAP, and it can also be used directly. Since Webflow’s acquisition of GSAP, the library including its plugins is free for everyone, and the GSAP installation documentation states that all plugins are freely available on npm and that users of the former private repository should move to GSAP 3.13 or later. A tutorial that asks for a paid membership or a trial build describes the earlier arrangement.

When GSAP is loaded with plain script tags, the GSAP documentation notes that plugins register themselves once the core has loaded, and still recommends calling gsap.registerPlugin() explicitly. Registering a plugin more than once is harmless, so the call is a cheap habit that also keeps working when the same code is later moved into a build step. Load the core first, then the plugins, and use defer on all of them so the order is preserved.

<!-- Page settings > Custom code > Before </body> tag, on the events page -->
<script defer src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/gsap.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/gsap@3.15.0/dist/ScrollTrigger.min.js"></script>
<script defer>
  window.addEventListener("DOMContentLoaded", function () {
    if (!window.gsap || !window.ScrollTrigger) { return; }
    gsap.registerPlugin(ScrollTrigger);
    gsap.from("[data-reveal]", {
      y: 24,
      opacity: 0,
      stagger: 0.12,
      scrollTrigger: { trigger: ".events-list", start: "top 80%" }
    });
  });
</script>

Before adding this, decide whether the panel can do it. If the effect is a scroll-triggered reveal with a stagger, Interactions with GSAP builds it visually and stores it in the project, where collaborators can see it. Custom code is for what the panel cannot express: data-driven animation, custom plugins and state machines. Do not run both against the same element, because two systems writing the same property produce a fight that no timeline editor will show. The same caution applies to respecting reduced motion: wrap custom animations in a check of window.matchMedia("(prefers-reduced-motion: reduce)") and skip them for visitors who ask for less motion.

Attribute-based libraries

A family of third-party libraries lets a designer enable behaviour without writing JavaScript, by adding custom attributes to elements in the Designer and loading one script. Typical features are filtering and sorting Collection lists beyond the native options, loading more items, copying text to the clipboard, table of contents, reading time and form helpers. Several independent vendors and community projects publish such libraries, some of them as open-source packages and some as hosted services, and they differ in licence, support and release rhythm. This page does not recommend a product; it explains how to judge any of them.

The mechanism is the one described in Custom Attributes and JavaScript: the Designer’s Settings panel lets you add a name and value to an element, the published HTML contains it, and the script finds the element by selector.

<!-- Element settings > Custom attributes, set in the Designer -->
<!--   data-fb-filter-list = "events"  on the Collection list wrapper -->
<!--   data-fb-filter-item = "bloom"   on the Collection item -->

<!-- Footer code: the library, pinned and protected -->
<script
  defer
  src="https://cdn.example.com/attr-lib@2.4.1/attr-lib.min.js"
  integrity="sha384-PLACEHOLDER"
  crossorigin="anonymous"></script>

Questions worth asking about an attribute library before adopting it:

  • Does the attribute contract survive an update? The attributes are an interface between your design and the script. Renamed attributes or changed defaults in a new version silently disable the behaviour, which is another reason to pin the version.

  • What happens when the script does not load? A filtered list that depends on the script should still show every item. Content hidden by a class that only the script removes becomes invisible when the script fails.

  • How does it interact with Webflow’s own behaviour? Collection lists paginate, load items per page (the native limits are 100 items per list) and may be nested; a library that reads the DOM sees only what the page contains. Verify behaviour with the real content volume.

  • Is it maintained, and who maintains it? Look at the release history and the licence before building on it. A tiny script you can read and keep in your own repository is sometimes the better answer.

  • Does it send data anywhere? A library for a form may transmit field values to a service. Read what it does before connecting it to a form that collects personal data.

Maps and booking widgets for Fernbrook Gardens

The Fernbrook Gardens site needs two third-party features on its visit-planning pages: a map showing the garden entrance and car parks, and a ticket-booking widget on the ticket page. Both load content from servers the garden does not control, both set cookies or read the visitor’s address in typical configurations, and both are therefore candidates for consent gating.

A map iframe

The simplest map is an iframe provided by a mapping service. It runs in its own browsing context, so it cannot read the page’s data, and it can be loaded lazily. In a Code Embed element:

<iframe
  title="Map of Fernbrook Gardens and its car parks"
  src="https://maps.example.com/embed?place=fernbrook-gardens"
  width="100%"
  height="420"
  loading="lazy"
  referrerpolicy="strict-origin-when-cross-origin"
  allowfullscreen></iframe>

The title attribute is what a screen reader announces for the frame, so it should describe the content. loading="lazy" keeps the frame from downloading until the visitor scrolls near it, which matters because a map is typically far below the fold. Setting a fixed height prevents the layout from shifting when the frame appears. The accessibility checklist maintained by Webflow lists custom code among the places where accessibility has to be checked by hand, because the Designer cannot audit an embed; test the keyboard focus order around a map and offer the address and opening directions as plain text beside it.

Booking widgets usually come as a script that fills a container element. Because the script contacts the provider as soon as it loads, a site that needs consent loads it only after the visitor has agreed. Webflow’s Browser API exposes the visitor’s choice for Webflow’s own Analyze and Optimize tracking through wf.getUserTrackingChoice(), wf.allowUserTracking() and wf.denyUserTracking(), and the documentation states the methods apply only when site tracking is enabled and that the sample integrations with consent platforms are not supported by Webflow and need customizing. For a third-party widget, a consent manager of your own choice is the source of truth. The pattern is the same either way: the script element is not present until the consent exists.

<div id="booking-widget" data-provider="tickets.example.com">
  <p>Ticket booking loads after you accept third-party content.</p>
  <button type="button" id="load-booking">Load the booking form</button>
  <p><a href="/visit/tickets-by-email">Prefer to book by email?</a></p>
</div>

<script>
  (function () {
    var button = document.getElementById("load-booking");
    if (!button) { return; }
    button.addEventListener("click", function () {
      var s = document.createElement("script");
      s.src = "https://tickets.example.com/widget/v3.2.0/widget.js";
      s.async = true;
      s.onload = function () {
        if (window.TicketWidget) {
          window.TicketWidget.mount("#booking-widget", { site: "fernbrook-gardens" });
        }
      };
      document.head.appendChild(s);
    });
  })();
</script>

Here consent is expressed by an explicit click that loads the widget, which also keeps the bytes out of the page for visitors who never book. A site-wide banner can replace the button: the banner’s accept handler runs the same loading code. Whichever you choose, the page still offers a way to book that needs no third-party script, in this case an email link, because a visitor who declines should not lose the ability to buy a ticket. Cookie and script handling in general, and where this code sits among the head and footer boxes, are covered in Custom Code: Head, Footer and Embeds.

Performance review

Third-party code is one of the commonest causes of a slow Webflow site, since the designer-built part is generated and optimised by the platform while the added code is not. Review each library against the user-centred measures grouped as Core Web Vitals: Largest Contentful Paint (how fast the main content appears), Interaction to Next Paint (how quickly the page reacts to input) and Cumulative Layout Shift (how much the layout jumps).

Symptom Likely third-party cause Remedy

Late Largest Contentful Paint

A render-blocking script or stylesheet in the head, or a widget above the fold

Add defer, move the script to the footer, load below-the-fold widgets lazily

Poor Interaction to Next Paint

A large library running long tasks on the main thread

Load only on the pages that need it, replace the library with a lighter one, delay it until first interaction

Layout shift

A map or widget without reserved space, a late font or stylesheet

Give the container a fixed height or aspect ratio, preload what is critical

Many requests

Several small libraries each from a different host

Remove duplicates, combine what you control, drop unused ones

Measure before and after each addition on the published site, using the speed audits of a browser’s developer tools and the field data that Webflow’s Analyze offers where it is enabled. The wider methods, including image conversion and lazy loading that Webflow performs for you, are in Performance Optimization. A good test is to publish with the library, test, remove it, test again, and keep the library only if the difference is justified by what it provides.

Privacy review

A request to a third-party host reveals, at minimum, the visitor’s network address, the page address (often through the referrer) and the browser’s identifiers. When the host is an advertising network, a map provider or a video platform, that is personal data under regulations such as the GDPR, and it is sent before the visitor has read a word of your privacy notice. For each added script or embed, record:

  1. Which host receives requests, and who operates it.

  2. Which data it receives: address, referrer, cookies, fields typed into a form.

  3. Its purpose and legal basis: strictly necessary (for example a payment form the visitor has asked for) or optional (analytics, marketing, map tiles).

  4. Whether consent is required before it loads, and how the page implements that (the booking example above gates the widget by a click).

  5. Whether the same function is possible without a third party, for example a static map image with a link, or a self-hosted font.

The result feeds the cookie and privacy notices, which must name the services in use. The legal analysis depends on your jurisdiction and is outside what this documentation can decide, so involve whoever is responsible for compliance at the garden. Self-hosting a library, as described above, removes one recipient but does not remove the privacy review of what the library itself does.

Security review

Any script you add runs with the same permissions as your own code. It can read what visitors type into forms, change links, and load further scripts. Supply-chain attacks target exactly this: a compromised library account or a hijacked CDN address changes a file that thousands of sites load, and every one of them executes the new code. The controls form a ladder:

  • Fewer scripts. A removed script cannot be compromised.

  • Pinned versions and SRI, so a changed file is refused instead of run.

  • Trusted sources only. Prefer the publisher’s own documented address over a mirror, and avoid scripts copied from forum posts.

  • Narrow scope. Page-level code limits the exposure to the pages that need it; keep scripts out of pages that include login-like or payment-like forms.

  • Isolation. An iframe confines a widget to its own browsing context; the sandbox attribute narrows its powers further.

  • A Content Security Policy (CSP), which tells the browser which origins may supply scripts, styles and frames.

A CSP can be expressed by a response header or, in a limited form, by a meta element with http-equiv="Content-Security-Policy" in the head, which MDN documents. A meta element cannot express every directive, and it protects only what is parsed after it, so place it first in the head code. Response headers are set by the host; the options for them on Webflow hosting are described in the Webflow documentation for your plan, and a self-managed proxy gives you full control, as described in Reverse Proxy and Custom Hosting.

<!-- Site settings > Custom code > Head code, as the first element -->
<meta http-equiv="Content-Security-Policy"
      content="script-src 'self' https://cdn.jsdelivr.net https://tickets.example.com; frame-src https://maps.example.com">

A policy that is too strict breaks the Webflow runtime and the site’s own inline scripts, so start in a test site and adjust. Adding a new third-party host later means editing the policy as well, which is a useful forcing function: nobody can add a script from a new origin without somebody noticing.

Review checklist

Use this table for every library, widget or embed before it is published, and keep the completed table with the site’s documentation.

Area Question Done

Need

Can the Designer, Interactions or a Webflow-native feature do this without a library?

[ ]

Scope

Is the code loaded only on the pages and in the boxes that need it?

[ ]

Loading

Does the script use defer (or async where it is independent), and is it kept out of the blocking head when it can be?

[ ]

Version

Is the version written in full in the address and noted in a comment?

[ ]

Integrity

Do scripts and stylesheets from another origin carry integrity and crossorigin="anonymous"?

[ ]

Source

Does the address belong to the publisher, with no mirror or copied snippet?

[ ]

Fallback

Is the page usable and readable if the library does not load?

[ ]

Performance

Were Core Web Vitals measured before and after, and is the difference justified?

[ ]

Layout

Is space reserved for the widget so the page does not shift?

[ ]

Privacy

Are the recipients, the data and the legal basis recorded, and is consent obtained before the script loads where it is required?

[ ]

Policies

Are the privacy and cookie notices updated to name the service?

[ ]

Security

Is the host allowed in the Content Security Policy, and is an iframe sandboxed where possible?

[ ]

Accessibility

Was the widget tested with the keyboard and a screen reader, and does it offer a text alternative?

[ ]

Ownership

Is someone named responsible for upgrading or removing it?

[ ]