# How htmx Executes Scripts After Swapping Content: A Deep Dive into the Source Code

> Discover how htmx executes scripts after swapping content by examining its source code. Learn how it safely injects script elements for immediate execution.

- Repository: [Big Sky Software/htmx](https://github.com/bigskysoftware/htmx)
- Tags: deep-dive
- Published: 2026-08-30

---

**When htmx swaps HTML content received from an AJAX response, it clones `<script>` elements and inserts them directly into the live DOM to trigger immediate execution while respecting Content Security Policy settings.**

When working with dynamic content updates in the **bigskysoftware/htmx** library, understanding how inline JavaScript gets executed is crucial for debugging and security. By default, htmx processes and runs scripts embedded in swapped HTML fragments, but this behavior can be configured or disabled entirely. The implementation relies on a sophisticated DOM manipulation strategy that ensures browser compatibility while supporting strict CSP environments.

## The DocumentFragment Pipeline and Script Detection

After htmx receives an AJAX response, it constructs a **`DocumentFragment`** from the returned HTML using the internal `makeFragment` utility. According to the source code in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js) (lines 636‑638), if the global configuration **`htmx.config.allowScriptTags`** is set to `true` (the default defined at line 160), the fragment is passed to the **`normalizeScriptTags`** function for processing.

If script execution is disabled via configuration, htmx strips all `<script>` elements from the fragment at lines 640‑641 before insertion, ensuring no code execution occurs during the swap operation.

## How normalizeScriptTags Executes Scripts

The `normalizeScriptTags` function (lines 577‑590) implements a three-step process to safely execute JavaScript after content insertion:

### Detecting Valid JavaScript Nodes

Before processing, htmx validates each script using **`isJavaScriptScriptNode`** (lines 66‑68). This utility checks that the script has a valid JavaScript MIME type (`text/javascript`, `module`, or empty type), ensuring that non-executable scripts or data blocks are ignored during the normalization process.

### Cloning Script Elements with duplicateScript

For each valid JavaScript node, htmx invokes **`duplicateScript`** (lines 549‑558) to create a fresh `<script>` element. This function:

- Copies all attributes from the original node
- Transfers the text content to the new element
- Forces `async = false` to guarantee synchronous execution
- Optionally injects a nonce from **`htmx.config.inlineScriptNonce`** for CSP compatibility

### Live DOM Insertion for Immediate Execution

The cloned script is inserted directly before the original node using `parent.insertBefore(newScript, script)`, and the original is immediately removed. Because the new element is created and inserted into the live DOM (not via a `<template>` element), browsers execute its code immediately. This technique circumvents browser limitations where scripts inside `<template>` tags remain inert when moved to the document.

## Configuration Options for Script Security

The **bigskysoftware/htmx** repository provides granular control over script execution through configuration flags that accommodate both development convenience and strict security policies.

### Enabling and Disabling Scripts with allowScriptTags

By default, **`htmx.config.allowScriptTags`** is `true` (line 160), allowing inline scripts to run automatically after swaps. To disable script execution entirely—for example, when implementing a strict Content Security Policy without `'unsafe-inline'`—set this configuration to `false`:

```javascript
htmx.config.allowScriptTags = false;

```

When disabled, htmx removes all `<script>` tags from swapped content at lines 640‑641, preventing any code execution regardless of the response content.

### CSP Compatibility with inlineScriptNonce

For environments requiring nonce-based CSP directives, htmx supports the **`htmx.config.inlineScriptNonce`** property. When configured, the `duplicateScript` function automatically adds the nonce attribute to cloned script elements:

```javascript
htmx.config.inlineScriptNonce = 'abc123';

```

This allows browsers to execute inline scripts that would otherwise be blocked by CSP policies requiring `'nonce-abc123'`.

## Practical Implementation Examples

### Default Script Execution

When `allowScriptTags` remains enabled (default), any JavaScript returned in an htmx response executes immediately:

```html
<button hx-get="/update" hx-swap="innerHTML">Load Content</button>

```

If `/update` returns:

```html
<div>
  <script>
    console.log('Executed after swap');
    document.dispatchEvent(new CustomEvent('contentLoaded'));
  </script>
  <p>New content loaded</p>
</div>

```

The console logs immediately upon swap completion, and the custom event fires as expected.

### Disabling Scripts for Strict CSP

To prevent script execution in environments with strict security requirements:

```javascript
// In your initialization code or htmx-config.js
htmx.config.allowScriptTags = false;

```

Now the same response above would render the paragraph but strip the `<script>` element entirely, preventing execution while maintaining the HTML structure.

### Using Nonce Attributes

For CSP-compliant inline script execution:

```javascript
htmx.config.inlineScriptNonce = document.querySelector('meta[name="csp-nonce"]').content;

```

This configuration ensures all dynamically inserted scripts receive the correct nonce attribute, satisfying `'nonce-source'` CSP directives without requiring `'unsafe-inline'`.

## Summary

- htmx creates a **DocumentFragment** from AJAX responses and processes scripts via **`normalizeScriptTags`** before DOM insertion.
- The **`duplicateScript`** function clones script elements to force immediate execution in the live DOM, setting `async=false` and preserving attributes.
- Script execution is controlled by **`htmx.config.allowScriptTags`** (default: `true` at line 160), which can be disabled to strip all scripts for CSP compliance.
- The **`htmx.config.inlineScriptNonce`** property enables nonce injection into cloned scripts for environments with strict Content Security Policies.
- Only scripts passing the **`isJavaScriptScriptNode`** check (lines 66‑68) are processed, ensuring type validation before execution.

## Frequently Asked Questions

### Does htmx execute scripts by default?

Yes. By default, **`htmx.config.allowScriptTags`** is set to `true` in the source code at line 160. When htmx swaps content containing `<script>` tags, it clones and inserts them into the live DOM to trigger immediate execution. You must explicitly set this configuration to `false` to disable this behavior.

### Why does htmx clone script elements instead of using the original?

htmx uses the **`duplicateScript`** function (lines 549‑558) to create fresh script elements because browsers typically do not execute scripts moved from a `<template>` or DocumentFragment into the live DOM. By creating a new element and inserting it directly with `parent.insertBefore()`, htmx ensures the browser recognizes and executes the script immediately. The cloning process also allows htmx to inject nonce attributes and force synchronous execution with `async = false`.

### How can I prevent script execution for CSP compliance?

Set **`htmx.config.allowScriptTags = false`** before any swaps occur. When disabled, htmx will strip all `<script>` elements from the response at lines 640‑641 of [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js), preventing any inline code execution. This configuration is essential for environments using strict Content Security Policies without `'unsafe-inline'` permissions.

### Do external scripts (src attribute) work with htmx swaps?

Yes. The **`duplicateScript`** function copies all attributes from the original script element, including `src`, `type`, and `defer`. When cloned and inserted into the DOM, external scripts load and execute according to their specified attributes. However, if `allowScriptTags` is `false`, these scripts are removed regardless of whether they are inline or external.