Build a micro-javascript frontend framework based on Web Components and no-build

Build a micro-javascript frontend framework based on Web Components and no-build

Develop a micro-frontend using Web Components without build steps

·

3 min read

Creating a basic JavaScript framework on Web Components with Shadow DOM and ES Modules, including client-side routing, requires a few steps. I shall outline how one can set up a framework.

Step 1: Setting Up Your Project

Create a brand new directory for your project and initialize it.

mkdir my-framework
cd my-framework
npm init -y

Step 2: Creating the Base Class for Components

Create a base class for your components. This one should extend HTMLElement and set up some basic functionality.

// src/MyComponent.js
export class MyComponent extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
  }

  connectedCallback() {
    this.render();
  }

  disconnectedCallback() {
    // Clean up if needed
  }

  render() {
    // This method should be overridden by subclasses
    this.shadowRoot.innerHTML = `<p>Override the render method in your component</p>`;
  }
}

Step 3: Creating a Simple Component

Create a simple component that extends our MyComponent base class.

// src/HelloWorld.js
import { MyComponent } from './MyComponent.js';

class HelloWorld extends MyComponent {
  render() {
    this.shadowRoot.innerHTML = `<p>Hello, World!</p>`;
  }
}

customElements.define('hello-world', HelloWorld);

Step 4: Creating the Router

Build a simple client-side router. This router will be bound to changes in the URL and load an appropriate component accordingly.

// src/Router.js
class Router {
  constructor(routes) {
    this.routes = routes;
    window.addEventListener('popstate', this.handleRoute.bind(this));
    this.handleRoute();
  }

  handleRoute() {
    const path = window.location.pathname;
    const route = this.routes[path] || this.routes['/404'];
    document.querySelector('router-view').innerHTML = `<${route}></${route}>`;
  }

  navigate(path) {
    window.history.pushState({}, path, path);
    this.handleRoute();
  }
}

export const createRouter = (routes) => new Router(routes);

Step 5: Create the Entry Point

Now it is time to create an entry point for your application. This file should import the components that will be used and configure the router.

// src/index.js
import './HelloWorld.js';
import { createRouter } from './Router.js';

const routes = {
  '/': 'hello-world',
  '/404': 'not-found' // Create this component or use a default one
};

const router = createRouter(routes);

document.addEventListener('DOMContentLoaded', () => {
  document.querySelectorAll('[route-link]').forEach(link => {
    link.addEventListener('click', (e) => {
      e.preventDefault();
      router.navigate(e.target.getAttribute('href'));
    });
  });
});

Step 6: Setting Up Your HTML

Create an HTML file to serve your application. This file will import the entry point module and set up the routing links.

<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>My Framework</title>
</head>
<body>
  <nav>
    <a href="/" route-link>Home</a>
    <a href="/404" route-link>Not Found</a>
  </nav>
  <router-view></router-view>
  <script type="module" src="./src/index.js"></script>
</body>
</html>

Step 7: Running the Application

Since we are using ES Modules, we can take advantage of modern browsers' native module support. Use a simple HTTP server to serve your application.

You can use a tool like http-server to serve the public directory.

npx http-server public

Outcome

Now you have a basic frontend javascript framework using Web Components with Shadow DOM, ES Modules, and client-side routing with no build step. Here is the summary of files that you should have:

  • my-framework/

    • src/

      • MyComponent.js

      • HelloWorld.js

      • Router.js

      • index.js

    • public/

      • index.html
    • package.json

Expanding the Framework

To expand this framework, consider adding:

  • State Management: Implement a simple state management solution.

  • Styling: Add scoped styles to components using the Shadow DOM.

  • HTTP Requests: Include utilities for making HTTP requests.

  • Advanced Routing: Implement more advanced routing features like nested routes and route guards.

This setup gives you a strong base for a modern, lightweight framework using Web Components, Shadow DOM, ES Modules, and client-side routing—with no build step.