Skip to main content
RapidDev - Software Development Agency
webflow-tutorials

Webflow Custom Code: Where to Add, Troubleshooting, and Best Practices

Webflow has five code injection points: Embed element (inline, 10K chars), Page Head Code (per-page <head>), Page Before </body> (per-page footer JS), Project Head Code (site-wide <head>), and Project Before </body> (site-wide footer JS). Embed previews in the Designer; head/footer code does not. Custom code requires a paid Basic plan or above.

What you'll learn

  • Choose the correct injection point for any custom code scenario (Embed vs Page Head vs Project Head)
  • Add CSS for properties not available in the Style Panel (::before, clip-path, transform-style)
  • Inject third-party scripts globally or on specific pages only
  • Debug code that works locally but fails in Webflow's environment
  • Understand execution order differences between head code and body-end code
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced11 min read30-45 minBasic plan or above (paid)March 2026RapidDev Engineering Team
TL;DR

Webflow has five code injection points: Embed element (inline, 10K chars), Page Head Code (per-page <head>), Page Before </body> (per-page footer JS), Project Head Code (site-wide <head>), and Project Before </body> (site-wide footer JS). Embed previews in the Designer; head/footer code does not. Custom code requires a paid Basic plan or above.

Webflow Custom Code: Injection Points, Execution Order, and Troubleshooting

Webflow's visual editor handles ~90% of CSS properties natively, but custom code becomes necessary for pseudo-elements (::before/::after), CSS properties with no visual control (clip-path, scroll-snap, transform-style), third-party scripts, analytics, chat widgets, and advanced JavaScript interactions. Getting custom code to work in Webflow requires knowing which of the five injection points to use, understanding execution order, and debugging why code that works in a standalone file fails in the Webflow environment. This tutorial covers all five injection points, their character limits and scopes, a practical troubleshooting framework, and the most common patterns like Embed vs head injection.

Prerequisites

  • Webflow site on a paid plan (Basic plan or above) — custom code is not available on free Starter sites
  • Basic understanding of HTML, CSS, and JavaScript
  • Published or preview URL for testing (most custom code does not execute in the Designer canvas)

Step-by-step guide

1

Understand the five injection points

Webflow has five places to add custom code, each with different scope and behavior: 1. Embed Element — inline in the page at a specific position. 10,000 character limit. CSS and JS both work. Renders in Designer preview mode AND on published site. JS does not execute in the Designer canvas itself. 2. Page Settings > Head Code — injected into the <head> of a specific page only. 20,000 character limit. NOT visible in Designer canvas preview. Executes on published and Webflow staging URLs. 3. Page Settings > Before </body> Code — injected before the closing </body> tag on a specific page. 20,000 characters. Best for page-specific JS. 4. Project Settings > Head Code — site-wide <head> injection. 20,000 characters. Runs on every page. Best for global analytics, custom fonts, or site-wide CSS. 5. Project Settings > Before </body> Code — site-wide footer JS. 20,000 characters. Runs on every page before body close.

Expected result: You can identify which injection point is appropriate for any given piece of custom code before you write or paste it.

2

Add site-wide custom CSS via Project Settings

For CSS that applies across your entire site — global style overrides, ::before/::after pseudo-elements, custom scrollbars, CSS animations — use Project Settings > Custom Code. Navigate to the top-left Webflow logo menu > Site Settings > Custom Code tab. In the 'Head Code' text area, add your CSS wrapped in a <style> tag. Example for a smooth scroll override: `<style> html { scroll-behavior: smooth; } </style>`. Example for a CSS custom property: `<style> :root { --brand-primary: #0057FF; } </style>`. Click 'Save Changes'. Changes are live after the next publish. Test on the published URL or staging preview — not the Designer canvas.

Expected result: Your CSS is applied site-wide after publishing. Use browser DevTools (right-click > Inspect) on the published URL to confirm the styles are present in the rendered <head>.

3

Add page-specific code via Page Settings

For code that should only load on one page — a page-specific analytics event, a Stripe checkout script, a specific map embed — use Page Settings. In the Designer, open the Pages panel (P key). Click the settings gear icon next to the page you want to target. Scroll to the 'Custom Code' section. Add CSS or analytics/initialization code in 'Head Code'. Add operational JS (that depends on the DOM being loaded) in 'Before </body> code'. Click 'Save'. Publish the site. The code appears only on that page, not site-wide — confirmed by viewing source on two different pages.

Expected result: Your code loads on the target page only. The additional script does not increase load time on other pages.

4

Use Embed elements for inline code and per-element CSS

The Embed element is the only injection point that: (a) is position-specific on the page, and (b) renders in Designer preview mode (not just published). Add it: Add Elements panel (A) > Basic > Embed. Click the Embed element on the canvas > a code editor panel opens. Paste your HTML, CSS (<style> block), or JavaScript (<script> block) and click 'Save & Close'. Best use cases: inline SVGs, third-party widget snippets (Calendly, Typeform embeds), per-element CSS overrides, HTML input types not available in Webflow (date picker, range slider). The 10,000-character limit applies — for larger code, use Page Settings instead.

Expected result: The Embed element renders its content in the Designer's Preview mode and on the published site. CSS inside a <style> tag in an Embed applies to elements on the same page.

5

Add CSS for properties not in the Style Panel

Several CSS properties have no visual control in Webflow's Style Panel and require custom code. Common examples: `transform-style: preserve-3d` — required for true 3D card flips: ```css .card-wrapper { transform-style: preserve-3d; } ``` `clip-path` — for non-rectangular shapes: ```css .hero-clip { clip-path: polygon(0 0, 100% 0, 100% 85%, 0 100%); } ``` `::before` and `::after` pseudo-elements: ```css .section-heading::before { content: ''; display: block; width: 48px; height: 4px; background: #0057FF; margin-bottom: 12px; } ``` Add these via Project Settings Head Code (site-wide) or Page Settings Head Code (page-specific) inside a <style> tag. The element's class in Webflow must exactly match the CSS selector.

Expected result: CSS properties unsupported in the visual Style Panel are applied correctly on the published site. Confirm in DevTools Computed Styles tab.

6

Load external libraries and execute scripts correctly

JavaScript that depends on DOM elements (querySelector, event listeners, manipulating elements) must run AFTER those elements are in the DOM. The correct injection sequence: For library CDN includes (no DOM dependency): Project Settings > Head Code with `<script src='...'>` tag. Loads early, available to subsequent scripts. For DOM-dependent initialization scripts: Project Settings > Before </body> Code OR Page Settings > Before </body> Code. At this point the DOM is fully built. For deferred loading (best for performance): use the `defer` attribute on script tags: `<script defer src='https://example.com/library.js'>`. Loads in parallel with HTML parsing, executes after parsing is complete. For code that should wait for the DOM to be interactive: wrap in a DOMContentLoaded listener: ```javascript document.addEventListener('DOMContentLoaded', function() { // Your initialization code here }); ```

Expected result: External libraries load and initialize without console errors. DOM-dependent code finds the target elements and runs without 'Cannot read property of null' errors.

7

Troubleshoot custom code that is not working

Systematic debugging workflow for code that is not working in Webflow: 1. Check injection point: Is the code in Head Code or Before </body>? Head code runs before DOM is built — DOM-dependent JS will fail there. Move it to Before </body>. 2. Test on the correct URL: Embed elements preview in Designer; everything else requires the published or Webflow staging URL (your-site.webflow.io). Designer canvas does NOT execute JS. 3. Open DevTools Console: Right-click > Inspect > Console. Check for error messages — 'Uncaught ReferenceError', 'is not a function', etc. 4. Check class name conflicts: Webflow's class names are lowercase, hyphenated. CSS selectors are case-sensitive. Use DevTools > Elements to inspect the actual rendered class name. 5. Check character limits: Head Code and Before </body> are limited to 20,000 characters. Embed is 10,000. Truncated code causes silent failures or syntax errors. 6. Verify the code works outside Webflow: paste it in a CodePen or JSFiddle first. If it fails there, the issue is in the code itself.

Expected result: You can systematically isolate whether the issue is injection point, execution timing, class name mismatch, character limit, or a code bug.

8

Add analytics and third-party marketing scripts correctly

For Google Analytics 4 (GA4): Project Settings > Custom Code > Head Code > paste the GA4 gtag.js script provided by Google Analytics. For Google Tag Manager: paste the GTM <script> snippet in Project Settings > Head Code and the <noscript> snippet in Project Settings > Before </body> Code (GTM provides both). For Facebook Meta Pixel: paste in Project Settings > Head Code. For HubSpot tracking: paste in Project Settings > Before </body> Code (HubSpot recommends body-end loading). For chat widgets (Intercom, Crisp, Zendesk): paste in Project Settings > Before </body> Code. These load on every page and track visitors site-wide.

Expected result: Analytics and marketing tags fire on every page load. Confirm in the GA4 Realtime report, Facebook Pixel Helper browser extension, or GTM Preview mode.

Complete working example

webflow-custom-code-examples.html
1<!-- =============================================
2 WEBFLOW CUSTOM CODE EXAMPLES
3 Add to Project Settings or Page Settings
4 ============================================= -->
5
6<!-- 1. SMOOTH SCROLL (Project Head Code) -->
7<style>
8 html { scroll-behavior: smooth; }
9</style>
10
11<!-- 2. TRANSFORM-STYLE: PRESERVE-3D (Project Head Code) -->
12<!-- Required for true 3D card flips not achievable with Children Perspective alone -->
13<style>
14 .card-wrapper { transform-style: preserve-3d; }
15 .card-front, .card-back { backface-visibility: hidden; }
16</style>
17
18<!-- 3. CLIP-PATH DIAGONAL SECTION (Project Head Code) -->
19<style>
20 .hero-diagonal {
21 clip-path: polygon(0 0, 100% 0, 100% 88%, 0 100%);
22 }
23</style>
24
25<!-- 4. BEFORE/AFTER PSEUDO-ELEMENT DECORATIVE LINE (Project Head Code) -->
26<style>
27 .section-title::before {
28 content: '';
29 display: block;
30 width: 48px;
31 height: 4px;
32 background: #0057FF;
33 border-radius: 2px;
34 margin-bottom: 16px;
35 }
36</style>
37
38<!-- 5. CUSTOM SCROLLBAR (Project Head Code) -->
39<style>
40 ::-webkit-scrollbar { width: 8px; }
41 ::-webkit-scrollbar-track { background: #f1f1f1; }
42 ::-webkit-scrollbar-thumb {
43 background: #888;
44 border-radius: 4px;
45 }
46 ::-webkit-scrollbar-thumb:hover { background: #555; }
47</style>
48
49<!-- 6. DOM-READY JS (Project Before </body> Code) -->
50<!-- Wrap DOM-dependent code to ensure elements exist -->
51<script>
52document.addEventListener('DOMContentLoaded', function() {
53 // Example: add active class to current nav link
54 var currentPath = window.location.pathname;
55 var navLinks = document.querySelectorAll('.nav-link');
56 navLinks.forEach(function(link) {
57 if (link.getAttribute('href') === currentPath) {
58 link.classList.add('is-active');
59 }
60 });
61});
62</script>
63
64<!-- 7. LOAD EXTERNAL LIBRARY + INITIALIZE (Project Before </body>) -->
65<script src="https://cdn.jsdelivr.net/npm/library@version/dist/library.min.js"></script>
66<script>
67document.addEventListener('DOMContentLoaded', function() {
68 // Initialize the library after DOM is ready
69 LibraryName.init({
70 target: '.target-class',
71 option: 'value'
72 });
73});
74</script>

Common mistakes

Why it's a problem: Adding JavaScript to Head Code and it failing with 'Cannot read property of null'

How to avoid: Head code runs before the DOM is built. Elements don't exist yet when the script executes. Move the script to Page Settings > Before </body> Code, or wrap it in: document.addEventListener('DOMContentLoaded', function() { /* your code */ });

Why it's a problem: Custom CSS class selector not matching the Webflow class and styles not applying

How to avoid: Webflow generates class names in lowercase with hyphens. Open DevTools > Elements panel on the published URL and inspect the actual class name on the element. Ensure your CSS selector matches exactly, including any combo classes Webflow appends.

Why it's a problem: Code works in CodePen but not in Webflow — Console shows no errors

How to avoid: Check the character limit. Project Settings head code is capped at 20,000 characters and Embed at 10,000. Webflow silently truncates at the limit, creating partial code that fails without an obvious error. Split large scripts into separate injection points or host the file externally and load via src attribute.

Why it's a problem: Third-party script bloating every page because it was added to Project Settings instead of Page Settings

How to avoid: Marketing/chat widgets needed only on specific pages (e.g., a Stripe checkout script on the /checkout page only) should go in Page Settings > Custom Code for that page. Project Settings code loads on every page — reserve it for truly global scripts like analytics.

Best practices

  • Always use the narrowest scope injection point — Page Settings before Project Settings; this reduces page weight on unrelated pages
  • Test all custom code on the published or preview URL, never in the Designer canvas — JS does not execute on the canvas
  • Wrap DOM-dependent JavaScript in DOMContentLoaded or place it in the Before </body> section to guarantee the DOM is ready
  • Check Webflow's Style Panel Custom Properties section before writing a custom CSS rule — it supports ~90% of properties without needing a <style> tag
  • Use the `defer` attribute on external script tags to prevent them from blocking page rendering
  • Comment your custom code blocks with the date added, what they do, and who added them — this saves time when debugging six months later
  • Keep head code lean — every byte in the <head> delays Time to First Byte; move non-critical scripts to Before </body>

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

I need to add custom CSS and JavaScript to a Webflow site. Specifically: [describe what you need, e.g., 'add a ::before pseudo-element to section headings', 'load a Splide.js carousel library', 'add Google Tag Manager']. Which of Webflow's five code injection points should I use for each, and what is the exact code to add including correct HTML wrapper tags?

Webflow Prompt

In Webflow, my custom code is not working. Here is what I added: [paste your code] and where I added it: [Embed / Page Head / Project Head / Before </body>]. The site is on [plan]. What I see on the published URL is: [describe symptom or error]. Diagnose the most likely cause and give me the corrected injection approach.

Frequently asked questions

Why doesn't my JavaScript work in the Webflow Designer canvas?

The Webflow Designer canvas is a sandboxed editing environment — JavaScript from Embed elements and custom code sections does not execute there for security reasons. CSS added via Embed renders in Designer preview mode, but JS only runs on the published URL (your-site.webflow.io or your custom domain) or in Webflow's Preview mode accessed via the eye icon. Always test JS on the preview or published URL.

Can I add custom code on the free Starter plan?

No. Custom code injection (Head Code, Before </body> Code in both Project and Page Settings, and Embed elements) requires a paid site plan — Basic plan or above. On the free Starter plan, the Custom Code tab exists in Site Settings but saving is disabled. The Embed element in the Add panel is visible but shows a 'Upgrade required' message when clicked.

What is the difference between Page Head Code and Project Head Code?

Project Settings > Head Code injects the same code into the <head> of every page on your site — ideal for site-wide analytics, global CSS, or libraries used everywhere. Page Settings > Head Code injects code only into that specific page's <head> — ideal for scripts only needed on one page (a Stripe.js script on the checkout page, a specific map library on the contact page). Using Page Settings reduces unnecessary script loading on all other pages.

How do I add CSS for ::before and ::after pseudo-elements in Webflow?

Webflow's Style Panel does not support pseudo-elements. Add them via custom CSS: open Project Settings > Custom Code > Head Code, and add a <style> block with your pseudo-element rules. The class name in your CSS must exactly match the Webflow class applied to the element (check in DevTools > Elements on the published URL). Example: .card-title::before { content: '→'; margin-right: 8px; color: #0057FF; }

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.