Six Native HTML Features That Replace Common JS UI libraries
Learn how popover, exclusive details, dialog, Declarative Shadow DOM, fetchpriority and datalist replace custom JavaScript, plus the limits each one still has.
This article is published in English.
Sources: HTML Features in 2026 That Most Devs Don’t Know Exist
For years HTML supplied structure and JavaScript supplied every behaviour: a modal meant a React library, a tooltip meant Popper.js or Tippy, and server-rendered components meant custom scripts. Meanwhile the WHATWG HTML Living Standard and browser engines added native building blocks that cover many of those cases with a few attributes, cutting boilerplate and bundle size while getting accessibility right by default. This guide covers six of them, the markup each needs, and where you still need CSS or JavaScript. Confirm browser support for your audience before dropping a fallback.
1. Popovers with the popover attribute
Tooltips and menus traditionally meant fighting z-index stacking contexts and hand-writing click-outside listeners. With the Popover API, any element carrying the popover attribute is promoted to the top layer when opened, above all regular content regardless of z-index. A button whose popovertarget names the element's id toggles it:
<!-- The Trigger -->
<button popovertarget="my-menu">Toggle Menu</button>
<!-- The Popover Content -->
<div id="my-menu" popover>
<p>I am natively managed! Press ESC or click outside to close me.</p>
</div>
Without any JavaScript you get:
- Declarative toggling: the button opens and closes the popover.
- Light dismiss: clicking outside or pressing Escape closes it. This applies to the default
popover="auto"mode;popover="manual"leaves closing to you. - No stacking battles: the open popover renders in the top layer above everything else.
Two limits: the API controls visibility and stacking, not placement, so anchoring a menu to its trigger still needs CSS (classic positioning or the newer, less widely supported anchor positioning). And a popover is non-modal and does not trap focus, so use dialog for confirmations.
2. Exclusive accordions with details and name
details and summary have long provided collapsible sections without JavaScript. The newer addition: details elements sharing a name form a group in which only one can be open, like a classic accordion.
<details name="faq">
<summary>What is modern HTML?</summary>
<p>A living standard evolving directly inside browser engines.</p>
</details>
<details name="faq">
<summary>Do I still need JavaScript?</summary>
<p>Much less of it than you used to!</p>
</details>
<details name="faq">
<summary>Is this supported natively?</summary>
<p>Yes, across all major modern browser engines.</p>
</details>
Opening one question closes any other open item with name="faq", with no listeners, state hooks or class toggling. Browsers without support for name simply allow several open items, a graceful fallback.
3. Accessible modals with dialog and ::backdrop
Custom div overlays regularly fail accessibility audits, because a correct modal must keep focus inside, block the page behind it and restore focus to the trigger afterwards. The dialog element handles this. Below, a form with method="dialog" closes the dialog on either button, and the pressed button's value becomes the dialog's returnValue:
<dialog id="user-modal">
<h2>Confirm Action</h2>
<p>Are you sure you want to delete this repository?</p>
<!-- A form inside a dialog with method="dialog" closes it automatically -->
<form method="dialog">
<button value="cancel">Cancel</button>
<button value="confirm">Delete</button>
</form>
</dialog>
<button onclick="document.getElementById('user-modal').showModal()">
Open Modal
</button>
The dimmed overlay is the ::backdrop pseudo-element, styled with plain CSS:
/* Styling the native browser backdrop */
dialog::backdrop {
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(4px);
}
showModal() puts the dialog in the top layer, makes the rest of the page inert, keeps focus inside and enables Escape to close; on close, current browsers return focus to the previously focused element. show(), by contrast, opens it non-modally without those protections. In React, call showModal() through a ref instead of an inline onclick.
4. Server-rendered Web Components with Declarative Shadow DOM
Shadow DOM once required client-side JavaScript (element.attachShadow({ mode: 'open' })), so server-rendered or statically generated Web Components had no encapsulated markup until scripts ran, causing unstyled flashes and layout shifts. Declarative Shadow DOM fixes this: a template with shadowrootmode inside the host element describes the shadow tree in HTML:
<user-card>
<template shadowrootmode="open">
<style>
/* Styles here stay encapsulated inside this component */
h3 { color: #3b82f6; font-family: sans-serif; }
</style>
<h3>Jane Doe</h3>
<p>Senior Frontend Engineer</p>
</template>
</user-card>
The parser converts the template into a shadow root on user-card before rendering, so the scoped styles and markup appear on first paint. Caveat: this happens only during HTML parsing. Markup inserted later via innerHTML creates no shadow root; dynamic insertion needs APIs such as setHTMLUnsafe(), so check current support.
5. Prioritizing critical resources with fetchpriority
loading="lazy" and async/defer cannot say which early resources matter most. With several images above the fold, the hero image behind your Largest Contentful Paint (LCP) should win over badges and icons. fetchpriority gives the network scheduler that hint:
<!-- Critical LCP Image: Load this immediately! -->
<img src="hero-banner.webp" fetchpriority="high" alt="Product Hero">
<!-- Lower priority images -->
<img src="avatar.webp" fetchpriority="low" alt="User avatar">
<!-- Critical JavaScript bundle -->
<script src="app.js" fetchpriority="high"></script>
This can improve Core Web Vitals without touching application logic. It is a hint, not a command, and marking many resources high cancels the benefit. Also never lazy-load the LCP image.
6. Native suggestions with datalist
Many projects add a heavy autocomplete component just to suggest values while allowing free text. datalist does this natively: link it via the input's list attribute and its options become suggestions.
<label for="framework">Choose or type a framework:</label>
<input list="frameworks" id="framework" name="framework" placeholder="e.g. Next.js">
<datalist id="frameworks">
<option value="Next.js"></option>
<option value="SvelteKit"></option>
<option value="Nuxt.js"></option>
<option value="Astro"></option>
<option value="Remix"></option>
</datalist>
The browser renders and filters the list, handles keyboard navigation and follows the platform's look. The trade-off is control: styling is minimal, behaviour differs across browsers and mobile, and rich options are impossible. Ideal for simple "pick or type" fields; a branded combobox still needs a component.
Native feature versus the old approach
- Tooltips and menus:
popoverinstead of a positioning library plus click-outside and Escape handling (positioning itself still comes from CSS). - Accordions:
detailselements sharing anameinstead of state and toggle logic in JavaScript. - Modals:
dialogwithshowModal()and::backdropinstead of a custom overlay with hand-written focus management. - Encapsulated server-rendered components:
template shadowrootmodeinstead of client-onlyattachShadow()calls. - Loading priorities:
fetchpriorityinstead of manual preload tricks or script-driven loading order. - Input suggestions:
datalistinstead of a custom autocomplete widget for simple cases.
For a broader look at platform features that can replace dependencies, see our overview of native browser APIs replacing popular npm packages.
Key takeaways
- Browsers now ship behaviour that once required libraries, often with focus handling and Escape-to-close built in.
- Native does not mean complete: popovers need CSS for placement,
datalistoffers little styling, and Declarative Shadow DOM only works at parse time. - Before adding a UI dependency, check whether one element or attribute covers the real requirement for your supported browsers.