DEV Community

Cover image for 8 native HTML elements you don't need a library for
Dimon
Dimon

Posted on • Originally published at dimonb19a.hashnode.dev

8 native HTML elements you don't need a library for

In my last post I wrote about staying close to the browser instead of fighting it. This is the concrete version: 8 native HTML elements that do what most projects install a library for.

I'm not anti-library. I'm anti-reinstalling-what-I-already-have. Here's the list.


1. <dialog> — instead of a modal library

The usual reach for an "are you sure?" prompt is a modal package. You don't need one. <dialog> does it natively: focus trapping, a styleable ::backdrop, Esc-to-close, and top-layer rendering.

<dialog id="confirm">
  <form method="dialog">
    <p>Delete this item?</p>
    <button value="cancel">Cancel</button>
    <button value="ok">Delete</button>
  </form>
</dialog>

<button onclick="confirm.showModal()">Delete</button>
Enter fullscreen mode Exit fullscreen mode

method="dialog" closes it and tells you which button was pressed. No library, no z-index wars.


2. <details> + <summary> — instead of an accordion library

Open/close, keyboard support, all free. And the newer name attribute makes a group mutually exclusive — only one open at a time, which is exactly the "accordion" behavior you'd normally write JS for.

<details name="faq"><summary>What is native-first?</summary><p></p></details>
<details name="faq"><summary>Do I still need JS?</summary><p></p></details>
Enter fullscreen mode Exit fullscreen mode

3. The Popover API — instead of a tooltip/dropdown-menu library

The popover attribute + popovertarget gives you a top-layer panel with light-dismiss, Esc-to-close, and proper stacking — with zero JavaScript. That's the part libraries used to own: open/close state, dismiss handling, z-index wars. Gone.

<button popovertarget="menu">Options</button>
<div id="menu" popover>
  <button>Rename</button>
  <button>Delete</button>
</div>
Enter fullscreen mode Exit fullscreen mode

What it doesn't solve yet is precise positioning — anchoring the panel to its trigger and flipping it when it hits a screen edge. CSS anchor positioning is landing for that, but support is still uneven, so I reach for Floating UI to handle placement. That's the honest split: the browser gives me the behavior for free, and I bring a small, focused library for the positioning math.


4. <select> and <datalist> — instead of a dropdown / autocomplete library

<select> is an accessible dropdown that works with the keyboard, uses the native picker on mobile, and submits with the form. <datalist> adds autocomplete suggestions to a plain input.

<input list="frameworks" />
<datalist id="frameworks">
  <option value="Svelte"></option>
  <option value="Astro"></option>
</datalist>
Enter fullscreen mode Exit fullscreen mode

Honest caveat: if you need a filterable, multi-select combobox, native isn't enough — that's one of the few places I still build custom.


5. <input type="range"> — instead of a slider library

A slider, with keyboard support, that you can fully style (track and thumb) in CSS now.

<input type="range" min="0" max="100" value="50" step="5" />
Enter fullscreen mode Exit fullscreen mode

6. <input type="color"> — instead of a color-picker library

A whole color-picker package is overkill for basic "pick a color." This is one tag.

<input type="color" value="#7c5cff" />
Enter fullscreen mode Exit fullscreen mode

7. <input type="date"> — instead of a date-picker library

A native calendar that's mobile-friendly out of the box.

<input type="date" min="2026-01-01" max="2026-12-31" />
Enter fullscreen mode Exit fullscreen mode

Honest caveat: styling the calendar is limited, and a date range needs two inputs. For heavy date UX you may still reach for help — but for "pick a date," the library was overkill.


8. loading="lazy" — instead of a lazy-loading library

This is my favorite, because it replaces an entire IntersectionObserver-based package with a single attribute.

<img src="big.jpg" loading="lazy" alt="…" />
Enter fullscreen mode Exit fullscreen mode

One word. That's the whole feature.


Where I still reach for a library

Native-first doesn't mean zero dependencies — it means I don't install a library to do something the platform already does. I still reach for focused, do-one-hard-thing libraries: Floating UI for positioning (above), and a handful of others for the genuinely-hard things the platform doesn't solve well yet. Those earn their place — and they're a post of their own, coming next.

The pattern

Most developers already have all of these — the platform just doesn't get checked first, out of habit. Native-first isn't "never install anything." It's: open the platform's docs before you open npm. Half the time the thing you were about to install is one tag away.

So next time you need a modal, a menu, a slider, or a color picker — try the native element first. You'll ship less code, get accessibility mostly for free, and have fewer things to maintain later.

And it only gets better

Here's what makes native-first a long-term bet, not just a tidiness preference: the platform keeps getting more capable, and you inherit that for free. The old "you can't style native elements" complaints are falling one by one — CSS now lets you animate <details>, auto-size a <textarea> with field-sizing, anchor-position a popover, and the customizable <select> (with ::picker()) is finally landing so you can style the dropdown itself.

A native element you adopt today quietly gets better with each browser release. A library you install is frozen the day you npm install it — and from then on it's your job to maintain. Betting on the platform is betting on something that improves without you touching your code.

Top comments (0)