reply View all Tutorials & Projects

How to Build a Counter Component with Web Components

In this video, learn how to build a basic counter component with JavaScript Web Components. If you're new to web components, this is a perfect beginners tutorial to start building your own custom elements and components.

Video Tutorial

Source Code

You can find the source code for this video below.

index.html
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Counter with Web Components</title>
	<script type="module" src="js/index.js"></script>
</head>
<body>
	<counter-component id="myCounter"></counter-component>
</body>
</html>
components/Counter.js
export default class Counter extends HTMLElement {
	css = `
		:host {
			display: block;
			max-width: 150px;
			background-color: white;
			border-radius: 4px;
			padding: 16px;
			border: 1px solid #dddddd;
			user-select: none;
		}

		.value {
			padding: 24px 0;
			text-align: center;
			font-family: sans-serif;
			font-size: 48px;
		}

		.buttons {
			display: flex;
			gap: 16px;
		}

		.button {
			flex-grow: 1;
			font-size: 24px;
			padding: 16px 0;
			background: #dddddd;
			color: #333333;
			cursor: pointer;
			outline: none;
			border: none;
			border-radius: 4px;
		}

		.button:active {
			background: #cccccc;
		}
	`

	template = () => {
		return `
			<div class="value">${this.value}</div>
			<div class="buttons">
				<button type="button" class="button button--increment">+</button>
				<button type="button" class="button button--decrement">-</button>
			</div>
		`;
	}

	constructor() {
		super();

		this.value = 0;

		this.attachShadow({ mode: "open" });
		this.render();
	}

	render() {
		this.shadowRoot.innerHTML = `
			<style>${this.css.trim()}</style>
			${this.template().trim()}
		`;

		this.shadowRoot.querySelector(".button--increment").addEventListener("click", this.onIncrementButtonClick);
		this.shadowRoot.querySelector(".button--decrement").addEventListener("click", this.onDecrementButtonClick);
	}

	onIncrementButtonClick = () => {
		this.value++;
		this.render();
	}

	onDecrementButtonClick = () => {
		this.value = Math.max(0, this.value - 1);
		this.render();
	}
}
index.js
import Counter from "./components/Counter.js";

customElements.define("counter-component", Counter);

const myCounter = document.getElementById("myCounter");

setTimeout(() => console.log(myCounter.value), 2000);

If you have any questions about this code, please leave a comment on the video.