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

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.

```sh
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.

```js
// 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.

```js
// 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.

```js
// 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.

```js
// 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.

```html
<!-- 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.

```sh
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.
