In modern web development, libraries like React have become the norm for building dynamic and interactive user interfaces. However, relying heavily on such libraries can sometimes lead to larger bundle sizes and reduced performance. By leveraging native web APIs, we can accomplish common design patterns more efficiently, enhancing performance and overall web compatibility. In this article, we will explore how to use these APIs to reduce our dependence on React hooks like useState
.
<input type="checkbox" />
HackOne often overlooked technique in native web development is the hidden <input type="checkbox" />
hack. This trick, which has been around for a while, offers a way to control UI state without relying on JavaScript, reducing bundle size and potentially improving performance.
Let’s start with a typical example in React:
import { useState } from "preact/hooks";
export default function Section() {
const [display, setDisplay] = useState(false);
return (
<div class="p-10 grid place-items-center gap-2">
<button onClick={() => setDisplay(!display)} class="btn">Click Me</button>
{display && <div>Hello!</div>}
</div>
);
}
In this example, the useState
hook and the onClick
handler are used to toggle the visibility of a piece of UI. While this approach is effective, it involves additional JavaScript, which can contribute to a larger bundle size.
Now, let’s refactor this example to use only native web APIs:
export default function Section() {
return (
<div class="p-10 grid place-items-center gap-2">
<input id="toggle" type="checkbox" class="peer hidden" />
<label for="toggle" class="btn">Click Me</label>
<div class="hidden peer-checked:inline">Hello!</div>
</div>
);
}
So, what’s happening here? We’ve replaced the useState
hook with a hidden <input type="checkbox" />
element. Here’s a breakdown of the changes:
<input type="checkbox" />
with an id
and the hidden
class. This checkbox will control the state.<label for="toggle">
is linked to the checkbox using the for
attribute, which toggles the checkbox state when clicked.peer
class, we can style elements based on the state of the checkbox. The peer-checked:inline
class makes the <div>
appear when the checkbox is checked.This final result from both the React and Native Web API are indistinguishable to the final user, only changing the performance/compatibility characteristics of the application. Also, this trick can be applied in Drawers, Modals and any other kind of boolean UI. Bellow are some examples of this usage:
1. Drawer
2. Modal
3. Coupon code
<input type="radio" />
HackAnother powerful yet often overlooked HTML element is the <input type="radio" />
. This can be especially useful for creating SKU selectors or similar UI components. Let’s examine how we can refactor a React component to use native radio inputs.
First, let’s look at a typical React component for a SKU selector:
import { useState } from "preact/hooks";
const options = ["1", "2", "3"];
export default function Section() {
const [selected, setSelected] = useState(0);
return (
<ul class="p-10 flex items-center justify-center gap-2">
{options.map((o, index) => (
<li key={index}>
<button
onClick={() => setSelected(index)}
class={`btn btn-circle ${index === selected ? "btn-primary" : ""}`}
>
{o}
</button>
</li>
))}
</ul>
);
}
In this example, the useState
hook and onClick
handlers are used to manage and update the selected state. This approach, while effective, involves additional JavaScript.
Now, let’s refactor this example to use <input type="radio" />
elements:
const options = ["1", "2", "3"];
export default function Section() {
return (
<ul class="p-10 flex items-center justify-center gap-2">
{options.map((o, index) => (
<li key={index}>
<input
id={o}
name="options"
type="radio"
class="peer hidden"
defaultChecked={index === 0}
/>
<label for={o} class="btn btn-circle peer-checked:btn-primary">
{o}
</label>
</li>
))}
</ul>
);
}
Here’s a breakdown of how we refactored the React component to use native web APIs:
<input type="radio" />
elements, each with a unique id
and the same name
attribute. The name
property ensures that only one radio button can be selected at a time within the group.hidden
class hides the radio buttons from view.<label for={o}>
is linked to the corresponding radio input using the for
attribute, making the label clickable to change the radio button’s state.defaultChecked
attribute to set the initial checked radio button.peer
class, we can style the labels based on the state of the radio inputs. The peer-checked:btn-primary
class applies the btn-primary
style to the selected label.Using the techniques presented in this article can lead to significant performance improvements for your website. By reducing the amount of JavaScript required, you decrease parse and compile times, thereby improving Total Blocking Time (TBT). Additionally, adding animations and transitions becomes simpler, as these can be managed directly within the DOM, eliminating the need for third-party libraries often required in React.
Another advantage of these native web API hacks is enhanced browser compatibility. Since these techniques are supported by all major browsers, your website will function consistently across different platforms, providing a seamless experience for all users. Furthermore, reducing dependency on external libraries can simplify your project and reduce potential security vulnerabilities, while better performance on low-end devices ensures a broader audience can have a good user experience.
However, there are some tradeoffs to consider. One drawback is the potential increase in initial DOM size, as all states must be represented in the DOM. This can lead to a higher First Contentful Paint (FCP), as the browser has to load more content initially. Additionally, while native web APIs are great for many use cases, they might not be suitable for highly interactive applications where complex state management and frequent updates are necessary. In such cases, frameworks like React offer more powerful and flexible solutions. SEO considerations also come into play, as you need to ensure that content visibility managed through the DOM is still accessible and indexable by search engines. Lastly, as your application scales, maintaining complex UI interactions purely through native web APIs and CSS might become challenging, making the abstractions provided by frameworks beneficial for managing complexity.
Using native web APIs like <input type="checkbox" />
and <input type="radio" />
can help reduce the dependency on JavaScript, resulting in smaller bundle sizes and potentially better performance. While this approach has its tradeoffs, it offers a streamlined and efficient way to handle common UI patterns.
Stay tuned for more insights into leveraging native web APIs for a more efficient web development process!