# How htmx Handles Attribute Inheritance: hx-inherit and hx-disinherit Explained

> Explore htmx attribute inheritance with hx-inherit and hx-disinherit. Understand how parent attributes cascade to children and how to control this behavior.

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

---

**htmx evaluates attribute inheritance through the `getAttributeValueWithDisinheritance` function in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js), which enables parent attributes to cascade to children by default while respecting `hx-disinherit` opt-outs and `hx-inherit` opt-ins when global inheritance is disabled.**

htmx attribute inheritance allows ancestor elements to share configuration attributes with their descendants, reducing repetitive markup in hypermedia-driven interfaces. According to the bigskysoftware/htmx source code, this mechanism relies on a DOM traversal algorithm that checks for inheritance exceptions at each level. Whether you need to prevent specific attributes from bubbling down or explicitly enable inheritance in restricted environments, understanding the interaction between `hx-inherit`, `hx-disinherit`, and the `htmx.config.disableInheritance` flag is essential for building maintainable applications.

## How Default Attribute Inheritance Works

By default, htmx automatically inherits attributes from parent elements. When an element triggers a request, htmx walks up the DOM tree looking for configuration attributes like `hx-target`, `hx-select`, or `hx-swap`. This cascading behavior minimizes attribute duplication across nested components.

The inheritance chain terminates when htmx encounters an explicit value on the current element or when an ancestor has disabled inheritance for that specific attribute. The default mode operates without requiring any special attributes, making parent-level configuration immediately available to all nested elements.

## The Inheritance Algorithm in src/htmx.js

The core logic resides in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js), specifically within the `getAttributeValueWithDisinheritance` function (lines 466–482). This routine determines whether an ancestor's attribute should be inherited based on the current configuration state.

### getAttributeValueWithDisinheritance Implementation

When htmx inspects an element's ancestors, it calls `getAttributeValueWithDisinheritance` with three parameters: the initial element, the current ancestor being inspected, and the attribute name being sought.

```javascript
// src/htmx.js – lines 466-482
function getAttributeValueWithDisinheritance(initialElement, ancestor, attributeName) {
    const attributeValue = getAttributeValue(ancestor, attributeName)
    const disinherit = getAttributeValue(ancestor, 'hx-disinherit')
    var inherit = getAttributeValue(ancestor, 'hx-inherit')
    if (initialElement !== ancestor) {
        // When global inheritance is disabled (htmx.config.disableInheritance)
        if (htmx.config.disableInheritance) {
            // only allow the attribute if the ancestor explicitly declares it
            // with hx-inherit="*" (all) or hx-inherit="attr1 attr2 …"
            if (inherit && (inherit === '*' || inherit.split(' ').indexOf(attributeName) >= 0)) {
                return attributeValue                // inherited
            } else {
                return null                           // not inherited
            }
        }
        // Normal mode – honour hx-disinherit
        // If the ancestor declares hx‑disinherit and the current attribute is listed,
        // the value is overridden with the sentinel string "unset"
        if (disinherit && (disinherit === '*' || disinherit.split(' ').indexOf(attributeName) >= 0)) {
            return 'unset'                           // disinherited
        }
    }
    return attributeValue                            // default inheritance
}

```

This function operates in two distinct modes:

- **Normal mode** (default): Inheritance is enabled globally, and `hx-disinherit` blocks specific attributes
- **Restricted mode**: Inheritance is disabled globally via `htmx.config.disableInheritance`, and `hx-inherit` selectively enables it

### getClosestAttributeValue and the 'unset' Sentinel

The DOM traversal is handled by `getClosestAttributeValue` (lines 90–98), which walks up the tree until it finds a definitive value:

```javascript
// src/htmx.js – lines 90-98
function getClosestAttributeValue(elt, attributeName) {
    let closestAttr = null
    getClosestMatch(elt, function(e) {
        return !!(closestAttr = getAttributeValueWithDisinheritance(elt, asElement(e), attributeName))
    })
    if (closestAttr !== 'unset') {
        return closestAttr
    }
}

```

The string `'unset'` serves as a sentinel value indicating that an ancestor explicitly forbids inheritance of that attribute. When `getAttributeValueWithDisinheritance` returns `'unset'`, the traversal continues upward, effectively ignoring that ancestor's value for the attribute in question.

## Using hx-disinherit to Block Inheritance

When inheritance is enabled (the default), `hx-disinherit` prevents specific parent attributes from cascading to descendants. This attribute accepts either a space-separated list of attribute names or the wildcard `*` to block all inheritance.

Consider a scenario where a parent defines a target but a child should not inherit it:

```html
<div hx-target="#out" hx-disinherit="hx-target">
  <button hx-get="/msg">Fetch</button>
</div>

```

In this example, the button does not inherit the parent's `hx-target="#out"` because the parent explicitly disinherits it. The `getAttributeValueWithDisinheritance` function returns `'unset'` when it encounters this declaration, causing htmx to continue searching up the DOM tree or use default values.

You can also block multiple specific attributes:

```html
<div hx-target="#out" hx-select="#content" hx-disinherit="hx-target hx-select">
  <button hx-get="/msg">Fetch</button>
</div>

```

Or disable all inheritance from a node using the wildcard:

```html
<div hx-target="#out" hx-disinherit="*">
  <button hx-get="/msg">Fetch</button>
</div>

```

## Using hx-inherit When Global Inheritance Is Disabled

When `htmx.config.disableInheritance` is set to `true`, the default behavior inverts: attributes no longer cascade automatically. In this mode, `hx-inherit` acts as an explicit opt-in mechanism, allowing specific attributes (or all attributes) to be inherited by descendants.

Enable restricted mode globally:

```javascript
htmx.config.disableInheritance = true;

```

Then selectively enable inheritance for specific attributes:

```html
<div hx-target="#out" hx-inherit="hx-target">
  <button hx-get="/msg">Fetch</button>
</div>

```

In this configuration, only `hx-target` is inherited by the button. Without the `hx-inherit` declaration, `getAttributeValueWithDisinheritance` returns `null`, and the attribute value is not passed down.

To enable inheritance for all attributes on a specific element, use the wildcard:

```html
<div hx-target="#out" hx-select="#content" hx-inherit="*">
  <button hx-get="/msg">Fetch</button>
</div>

```

The wildcard `*` allows every htmx attribute defined on that ancestor to cascade to descendants, effectively simulating default behavior for that subtree while maintaining strict isolation elsewhere in the application.

## Working Code Examples

### Example 1: Selective Opt-Out with hx-disinherit

Prevent a parent swap strategy from affecting a specific button while keeping other inherited attributes:

```html
<div hx-target="#modal" hx-swap="innerHTML" hx-disinherit="hx-swap">
  <button hx-get="/alert">Inherits target, not swap</button>
  <button hx-get="/banner" hx-swap="outerHTML">Custom swap</button>
</div>

```

The first button inherits `hx-target="#modal"` but uses the default swap behavior rather than `innerHTML`.

### Example 2: Global Lockdown with Selective Opt-In

Disable inheritance globally, then enable it only where needed:

```javascript
<script>
  htmx.config.disableInheritance = true;
</script>

<div hx-target="#default" hx-inherit="hx-target">
  <button hx-get="/page">Uses #default target</button>
</div>

<div hx-target="#other">
  <button hx-get="/other">Does not inherit target</button>
</div>

```

The first button inherits the target because its parent declares `hx-inherit="hx-target"`. The second div's target is not inherited by its button, which must specify its own target or use htmx defaults.

### Example 3: Wildcard Inheritance Control

Use `*` to manage inheritance scopes efficiently:

```html
<!-- Block all inheritance from this container -->
<section hx-disinherit="*">
  <div hx-target="#local-only">
    <button hx-get="/data">Only sees #local-only if defined on this div</button>
  </div>
</section>

<!-- Enable all inheritance when globally disabled -->
<div hx-inherit="*" hx-target="#global" hx-select="#content" hx-swap="beforeend">
  <button hx-get="/append">Inherits target, select, and swap</button>
</div>

```

## Summary

- **Default behavior**: Attributes automatically cascade from ancestors to descendants through the `getClosestAttributeValue` traversal in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js).
- **hx-disinherit**: Blocks inheritance for specified attributes (or all with `*`) when global inheritance is enabled, returning the sentinel `'unset'` value.
- **hx-inherit**: Enables inheritance for specific attributes (or all with `*`) only when `htmx.config.disableInheritance` is `true`.
- **Implementation**: The `getAttributeValueWithDisinheritance` function (lines 466–482) checks both directives and the global configuration to determine inheritance eligibility.
- **Sentinel value**: The string `'unset'` tells htmx to ignore an ancestor's value and continue searching up the DOM tree.
- **Testing**: Repository tests in [`test/attributes/hx-inherit.js`](https://github.com/bigskysoftware/htmx/blob/main/test/attributes/hx-inherit.js) and [`test/attributes/hx-disinherit.js`](https://github.com/bigskysoftware/htmx/blob/main/test/attributes/hx-disinherit.js) verify these behaviors.

## Frequently Asked Questions

### How do I prevent all htmx attributes from being inherited by child elements?

Use `hx-disinherit="*"` on the parent element. According to the source code in [`src/htmx.js`](https://github.com/bigskysoftware/htmx/blob/main/src/htmx.js), this wildcard tells `getAttributeValueWithDisinheritance` to return `'unset'` for every attribute lookup, effectively isolating the subtree from parent configuration while maintaining the default inheritance mode for the rest of the application.

### Why does hx-inherit not work when htmx.config.disableInheritance is false?

`hx-inherit` is designed specifically as an opt-in mechanism for restricted environments. When `disableInheritance` is `false` (the default), inheritance already occurs automatically, making `hx-inherit` redundant. The function `getAttributeValueWithDisinheritance` only evaluates the `hx-inherit` variable when the global configuration flag is set to `true`.

### What happens when both hx-inherit and hx-disinherit are present on the same element?

The source code evaluates these attributes in mutually exclusive branches based on the `htmx.config.disableInheritance` flag. If global inheritance is disabled, only `hx-inherit` is processed; if enabled, only `hx-disinherit` is processed. They do not conflict because they operate in different configuration modes, though colocating them would create confusion and is not recommended.

### Can I inherit some attributes while blocking others in the same component hierarchy?

Yes. In default mode, use `hx-disinherit="attr1 attr2"` to block specific attributes while allowing others to cascade. In restricted mode (with `htmx.config.disableInheritance = true`), use `hx-inherit="attr1 attr2"` to allow only those specific attributes to be inherited. Both attributes accept space-separated lists to fine-tune which configuration values cross component boundaries.