Website Frontend Development with Vanilla JavaScript

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Vanilla JavaScript Frontend Website Development

Vanilla JavaScript is native browser API without abstractions on top. No framework, no virtual DOM, no magic. When developer writes Vanilla JS, they work directly with what browser provides: document.querySelector, fetch, IntersectionObserver, CustomEvent, Web Components.

This is not nostalgia for pre-React era. This is conscious choice for projects with specific requirements: minimal bundle, maximum control, zero dependencies.

When Vanilla JS is justified

  • Widgets and embed scripts — code embedded on third-party sites, can't conflict with their frameworks
  • Libraries for npm publication — React dependency will bloat library for Vue or Svelte users
  • High-load animations — direct access to Canvas API, WebGL, Web Animations API
  • Browser extensions — Content Scripts run in isolated environment, frameworks add risks
  • Static sites with minimal interactivity — no need to drag 50 KB for one dropdown

Modern Vanilla JS is not 2010

Browser API in last 10 years grew dramatically:

// Lazy loading with IntersectionObserver
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      const img = entry.target;
      img.src = img.dataset.src;
      observer.unobserve(img);
    }
  });
}, { rootMargin: '200px' });

document.querySelectorAll('img[data-src]').forEach(img => observer.observe(img));
// Fetch with AbortController and timeout
async function fetchWithTimeout(url, options = {}, timeoutMs = 5000) {
  const controller = new AbortController();
  const timeout = setTimeout(() => controller.abort(), timeoutMs);

  try {
    const response = await fetch(url, { ...options, signal: controller.signal });
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return await response.json();
  } finally {
    clearTimeout(timeout);
  }
}

Web Components — component model without framework

class ProductCard extends HTMLElement {
  static get observedAttributes() { return ['product-id']; }

  connectedCallback() {
    this.#render();
    this.#attachEvents();
  }

  attributeChangedCallback(name, oldVal, newVal) {
    if (name === 'product-id' && oldVal !== newVal) {
      this.#fetchProduct(newVal);
    }
  }

  async #fetchProduct(id) {
    const data = await fetchWithTimeout(`/api/products/${id}`);
    this.#update(data);
  }

  #render() {
    this.innerHTML = `
      <article class="product-card">
        <img class="product-card__img" alt="">
        <h3 class="product-card__name"></h3>
        <span class="product-card__price"></span>
        <button class="product-card__btn">Add to cart</button>
      </article>
    `;
  }

  #update({ name, price, image }) {
    this.querySelector('.product-card__img').src = image;
    this.querySelector('.product-card__name').textContent = name;
    this.querySelector('.product-card__price').textContent = `${price} $`;
  }

  #attachEvents() {
    this.querySelector('.product-card__btn').addEventListener('click', () => {
      this.dispatchEvent(new CustomEvent('add-to-cart', {
        bubbles: true,
        detail: { productId: this.getAttribute('product-id') }
      }));
    });
  }
}

customElements.define('product-card', ProductCard);

Usage in HTML: <product-card product-id="42"></product-card>. Works in any framework or without one.

State management without Redux

// Simple reactive store via Proxy
function createStore(initialState) {
  const listeners = new Set();

  const state = new Proxy(structuredClone(initialState), {
    set(target, key, value) {
      target[key] = value;
      listeners.forEach(fn => fn(structuredClone(target)));
      return true;
    }
  });

  return {
    state,
    subscribe: (fn) => { listeners.add(fn); return () => listeners.delete(fn); },
    getSnapshot: () => structuredClone(state),
  };
}

const cartStore = createStore({ items: [], total: 0 });

cartStore.subscribe(state => {
  document.getElementById('cart-count').textContent = state.items.length;
});

Project structure with ESModules

src/
  components/
    product-card.js    // Web Component
    modal.js
  lib/
    store.js
    api.js
    utils.js
  pages/
    catalog.js
    product.js
  app.js
// package.json — minimal
{
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vite build"
  },
  "devDependencies": {
    "vite": "^5.0.0"
  }
}

Zero runtime dependencies. Vite only for development and build.

Implementation timeline

  • Week 1: module architecture, basic Web Components, API client
  • Weeks 2–3: business logic, interactive components, animations
  • Week 4: optimization (bundle splitting, prefetch), testing (Vitest + jsdom)

Final bundle for average project — 20–40 KB without external dependencies. LCP < 1s even on slow connection.