Skip to content

web-widget/web-widget

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Web Widget

CI npm version TypeScript License: MIT codecov Node.js Deno Bun WinterCG RFC Compliant

🌟 A revolutionary meta-framework that seamlessly integrates multiple frontend technologies with unprecedented simplicity.

Break free from technology stack lock-in while maintaining elegant simplicity. Experience the power of running React, Vue, and other frameworks together with ease.

⚠️ Preview Release: This project is currently in preview stage with API subject to changes. Some features mentioned may be under development.

πŸ“‹ Table of Contents

πŸ’« Design Philosophy: Simplicity Meets Power

Our framework is built on the core principle that powerful technology should be intuitive to use:

🎯 Simple by Design

  • Two File Types: Just @route.* and @widget.* - that's all you need to learn
  • Minimal Configuration: Works with sensible defaults and simple setup
  • Familiar Syntax: Use the frameworks you already know and love
  • Intuitive APIs: If it feels natural, it probably works

⚑ Powerful by Nature

  • Multi-Framework: React 19, Vue 3, Vue 2 - with more frameworks coming soon
  • Web Standards: Built on solid foundations that won't become obsolete
  • Real-World Ready: Designed to handle production applications
  • Future Proof: Open architecture that evolves with the web platform

"The best technology is the one you don't have to think about" - This is our guiding principle.

✨ Why Web Widget?

The Challenge: Modern web development often forces teams into framework silos, making technology evolution difficult and risky. Monolithic frontend architectures become increasingly hard to maintain and upgrade.

The Web Widget Approach: A meta-framework that embraces framework diversity while maintaining simplicity.

🎯 Core Benefits

  • πŸ”„ Technology Freedom: Use the right framework for each component, not the entire application
  • ⚑ Incremental Adoption: Introduce new frameworks gradually without big-bang rewrites
  • 🧩 Component Interoperability: Mix React and Vue components seamlessly
  • 🌐 Standards Foundation: Built on Web Standards that evolve with the platform
  • 🎯 Developer Experience: Simple concepts that scale to complex applications

πŸ’‘ Real-World Validation

Applications like insmind.com and gaoding.com demonstrate these benefits in practice:

  • Legacy component preservation while adopting modern frameworks
  • Zero-downtime migrations through incremental transitions
  • Successful mixing of multiple technology stacks in production
Application Architecture Use Case
insmind.com React pages + Vue 3 + Vue 2 components "Seamlessly integrated legacy Vue 2 components with modern Vue 3 features"
gaoding.com React pages + Vue 2 + Lit components "Migrated incrementally from Vue 2 to React without downtime"

πŸ’‘ These examples showcase the framework's ability to handle complex multi-technology scenarios in real applications.

The approach works: Web Widget provides a practical path to multi-framework architecture without the usual complexity overhead.

πŸš€ Quick Start

Get started with Web Widget (preview release):

# Clone or download the example
git clone https://github.com/web-widget/web-widget.git
cd web-widget/examples/react  # or examples/vue

# Install dependencies
npm install

# Start development
npm run dev

🎯 Current Framework Support

Framework Status Version
React βœ… Supported 19.x
Vue βœ… Supported 3.x, 2.x
Svelte 🚧 Coming Soon -
Solid 🚧 Coming Soon -
Angular 🚧 Planned -

πŸ’‘ Preview Status: Web Widget is currently in preview. A dedicated CLI tool (create-web-widget-app) is planned for future releases.

Try online examples:

Example Description Live Demo
React React pages with React + Vue components Open in StackBlitz
Vue Vue pages with React + Vue components Open in StackBlitz

πŸ—οΈ Core Architecture: Simplicity in Action

Web Widget's power comes from just two concepts - keeping it beautifully simple:

πŸ“„ Route Modules (*@route.*)

Server-side modules for rendering pages and handling HTTP requests.

// routes/index@route.tsx - Simple, yet it can do everything
import { defineRouteComponent } from '@web-widget/helpers';
import Counter from './components/Counter@widget.tsx';

export default defineRouteComponent(function HomePage() {
  return (
    <html>
      <body>
        <h1>Welcome to Web Widget</h1>
        <Counter count={0} />
      </body>
    </html>
  );
});

🧩 Widget Modules (*@widget.*)

Isomorphic components that work on both server and client - the secret to our power.

// components/Counter@widget.tsx (React)
import { useState } from 'react';

export default function Counter({ count }: { count: number }) {
  const [value, setValue] = useState(count);

  return (
    <div>
      <button onClick={() => setValue((v) => v - 1)}>-</button>
      <span>{value}</span>
      <button onClick={() => setValue((v) => v + 1)}>+</button>
    </div>
  );
}
<!-- components/Counter@widget.vue (Vue) -->
<script setup lang="ts">
import { ref } from 'vue';

const props = defineProps<{ count: number }>();
const value = ref(props.count);
</script>

<template>
  <div>
    <button @click="value--">-</button>
    <span>{{ value }}</span>
    <button @click="value++">+</button>
  </div>
</template>

πŸ”€ Cross-Framework Magic: Power Unleashed

The real power emerges when you effortlessly combine different frameworks:

// Mix React and Vue in the same page
import ReactCounter from './Counter@widget.tsx';
import VueCounter from './Counter@widget.vue';
import { toReact } from '@web-widget/vue';

const RVueCounter = toReact(VueCounter);

export default defineRouteComponent(function MixedPage() {
  return (
    <div>
      <h2>React Component:</h2>
      <ReactCounter count={0} />

      <h2>Vue Component (as React):</h2>
      <RVueCounter count={0} />
    </div>
  );
});

πŸ”₯ Key Features

⚑ Lightning Fast Performance

  • Streaming SSR: Pages start rendering before all data is loaded
  • Selective Hydration: Only interactive components hydrate on client
  • Optimized Bundles: Server components reduce client-side JavaScript
  • Zero Hydration Errors: End-to-end state caching eliminates SSR mismatches
  • HTTP Caching: Standards-based page caching with stale-while-revalidate patterns

πŸ”„ Technology Flexibility Without Complexity

  • Currently Supported: React 19, Vue 3, Vue 2 (Svelte, Solid, and more coming soon)
  • Progressive Migration: Upgrade frameworks piece by piece
  • Component Interop: Share components across different frameworks
  • No Lock-in: Each component can use its preferred framework

🌐 Web Standards First - Built to Last

  • WinterCG Compatible: Runs in Node.js, Deno, Bun, and Edge environments (Currently only Node.js deployment examples are supported, other platforms coming soon)
  • ESM Native: Modern module system with import maps
  • Web APIs: Use standard fetch, streams, and crypto APIs everywhere
  • Future Proof: Based on standards that won't become obsolete
  • Production Module Sharing: Import Maps for optimal production performance

πŸ”§ Production Ready, Developer Friendly

  • Type Safe: Full TypeScript support out of the box
  • File-based Routing: Intuitive routing with automatic route generation
  • Error Boundaries: Comprehensive error handling and fallbacks
  • Sensible Defaults: Simple setup with intelligent defaults that just work
  • Smart Bundling: Automatic dependency deduplication and sharing

πŸš€ End-to-End State Caching: Zero Hydration Errors

Web Widget solves SSR's biggest challenge: hydration mismatches. Our cache providers ensure server and client always render identical content.

🎯 The Problem Solved

// ❌ Traditional SSR: Hydration mismatches
function UserProfile({ userId }) {
  const [user, setUser] = useState(null); // Server: null, Client: fetched data

  useEffect(() => {
    fetchUser(userId).then(setUser); // Only runs on client
  }, []);

  return <div>{user?.name || 'Loading...'}</div>; // Different on server vs client
}

// βœ… Web Widget: Perfect hydration
function UserProfile({ userId }) {
  const user = syncCacheProvider(`user-${userId}`, () => fetchUser(userId));

  return <div>{user.name}</div>; // Identical on server and client
}

πŸ”„ How It Works

  1. Server: Execute data fetching, cache results
  2. Transfer: Automatically embed cached data in HTML
  3. Client: Read cached data, skip re-fetching
// Vue 3: Use asyncCacheProvider for top-level await
const userData = await asyncCacheProvider('user-profile', async () => {
  return fetch('/api/user').then((r) => r.json());
});

// React: Use syncCacheProvider for hook-like behavior
const userData = syncCacheProvider('user-profile', async () => {
  return fetch('/api/user').then((r) => r.json());
});

πŸš€ Key Benefits

  • βœ… Zero Hydration Errors: Perfect server-client state synchronization
  • βœ… Simple Setup: Framework handles most configuration automatically
  • βœ… Optimal Performance: Data fetched once, used everywhere
  • βœ… Type Safe: Full TypeScript support with inferred types

πŸ“ Project Structure: Elegant Organization

my-web-widget-app/
β”œβ”€β”€ routes/
β”‚   β”œβ”€β”€ (components)/
β”‚   β”‚   β”œβ”€β”€ BaseLayout.tsx
β”‚   β”‚   β”œβ”€β”€ Counter@widget.tsx
β”‚   β”‚   └── Counter@widget.vue
β”‚   β”œβ”€β”€ index@route.tsx
β”‚   β”œβ”€β”€ about@route.tsx
β”‚   β”œβ”€β”€ action/
β”‚   β”‚   β”œβ”€β”€ index@route.tsx
β”‚   β”‚   └── functions@action.ts
β”‚   └── api/
β”‚       └── hello@route.ts
β”œβ”€β”€ public/
β”œβ”€β”€ entry.client.ts
β”œβ”€β”€ entry.server.ts
β”œβ”€β”€ routemap.server.json
β”œβ”€β”€ importmap.client.json
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
└── vite.config.ts

Organized structure with clear separation of concerns.

πŸ“– Documentation

πŸ›£οΈ Routing & Navigation

File-System Routing

Web Widget supports file-system based routing conventions, automatically generating routemap.server.json during development.

File Naming Rules

File Name Route Pattern Matching Paths
index@route.ts / /
about@route.ts /about /about
blog/[slug]@route.ts /blog/:slug /blog/foo, /blog/bar
blog/[slug]/comments@route.ts /blog/:slug/comments /blog/foo/comments
old/[...path]@route.ts /old/:path* /old/foo, /old/bar/baz
[[lang]]/index@route.ts /{/:lang}? /, /en, /zh-cn

Route Groups

Create route groups using parentheses-wrapped folder names:

└── routes
    β”œβ”€β”€ (middlewares)
    β”‚   └── [...all]@middleware.ts # -> /:all*
    β”œβ”€β”€ (vue2)
    β”‚   β”œβ”€β”€ package.json
    β”‚   └── marketing@route.vue    # -> /marketing
    └── (vue3)
        β”œβ”€β”€ package.json
        └── info@route.vue         # -> /info
Route Module Examples

Basic Route Module

// ./routes/index@route.tsx
import { defineRouteComponent, defineMeta } from '@web-widget/helpers';
import BaseLayout from './components/BaseLayout';

export const meta = defineMeta({
  title: 'Home - Web Widget',
});

export default defineRouteComponent(function Page() {
  return (
    <BaseLayout>
      <h1>Welcome to Web Widget</h1>
      <p>This is a basic route module example</p>
    </BaseLayout>
  );
});

Data Fetching and Processing

// ./routes/fetch@route.tsx
import {
  defineRouteComponent,
  defineRouteHandler,
  defineMeta,
} from '@web-widget/helpers';
import BaseLayout from './components/BaseLayout';

interface PageData {
  items: Array<{ title: string; url: string }>;
}

async function fetchData(url: URL): Promise<PageData> {
  const response = await fetch(`${url.origin}/api/data`);
  return response.json();
}

export const meta = defineMeta({
  title: 'Data Fetching Example',
});

export const handler = defineRouteHandler<PageData>({
  async GET(ctx) {
    const data = await fetchData(new URL(ctx.request.url));

    const response = ctx.html(data);
    response.headers.set('X-Custom-Header', 'Hello');

    return response;
  },
});

export default defineRouteComponent<PageData>(function Page({ data }) {
  return (
    <BaseLayout>
      <h1>Data Fetching</h1>
      <ul>
        {data.items.map((item, index) => (
          <li key={index}>
            <a href={item.url}>{item.title}</a>
          </li>
        ))}
      </ul>
    </BaseLayout>
  );
});

Route Configuration

Routes are automatically configured based on your file structure. Web Widget generates the routing configuration during development, so you don't need to manually manage route mappings.

Advanced Routing Features

Dynamic Routes

// routes/users/[id]@route.tsx
export default defineRouteComponent(function UserPage(props) {
  const { id } = props.params;
  return <div>User ID: {id}</div>;
});

Navigation and Redirects

// Simple redirects in route handlers
export const handler = defineRouteHandler({
  async GET(ctx) {
    if (shouldRedirect) {
      return redirect('/new-path', 301);
    }
    return ctx.html();
  },
});

Error Handling

// Route-level error handling
export const handler = defineRouteHandler({
  async GET(ctx) {
    if (!data) {
      throw createHttpError(404, 'Not Found');
    }
    return ctx.html(data);
  },
});

Page Metadata Management

export const meta = defineMeta({
  title: 'My Page',
  description: 'Page description',
});

// Dynamic metadata in handlers
export const handler = defineRouteHandler({
  async GET(ctx) {
    const newMeta = mergeMeta(ctx.meta, {
      title: `User: ${user.name}`,
    });
    return ctx.html(null, { meta: newMeta });
  },
});

🧩 Component Development

Widget Module Examples

React Widget with Styles

// ./components/Counter@widget.tsx
import { useState } from 'react';
import styles from './Counter.module.css';

interface CounterProps {
  count: number;
}

export default function Counter(props: CounterProps) {
  const [count, setCount] = useState(props.count);

  return (
    <div className={styles.counter}>
      <button onClick={() => setCount(count - 1)}>βˆ’</button>
      <span className={styles.count}>{count}</span>
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}

Vue Widget with Scoped Styles

<!-- ./components/Counter@widget.vue -->
<script setup lang="ts">
import { ref } from 'vue';

interface CounterProps {
  count: number;
}

const props = defineProps<CounterProps>();
const count = ref(props.count);
</script>

<template>
  <div class="counter">
    <button @click="count--">βˆ’</button>
    <span class="count">{{ count }}</span>
    <button @click="count++">+</button>
  </div>
</template>

<style scoped>
/*...*/
</style>

Using Widgets in Routes

// ./routes/index@route.tsx
import { defineRouteComponent } from '@web-widget/helpers';
import BaseLayout from './components/BaseLayout';
import ReactCounter from './components/Counter@widget.tsx';
import VueCounter from './components/Counter@widget.vue';
import { toReact } from '@web-widget/vue';

const RVueCounter = toReact(VueCounter);

export default defineRouteComponent(function Page() {
  return (
    <BaseLayout>
      <h1>Mixed Technology Stack Example</h1>

      <h2>React Component:</h2>
      <ReactCounter count={0} />

      <h2>Vue Component:</h2>
      <RVueCounter count={0} />
    </BaseLayout>
  );
});
Advanced Widget Features

Render Control

// Server-only rendering
<StaticChart renderStage="server" data={chartData} />

// Client-only rendering
<InteractiveMap renderStage="client" location={coords} />

Working with Context

Access request data, parameters, and state in your components:

import { context } from '@web-widget/helpers/context';

export default function MyComponent() {
  const { request, params, state } = context();
  return <div>Current URL: {request.url}</div>;
}

🌐 Web Standards & APIs

Web Standards APIs

Full Web Standards support in all environments:

  • Network: fetch, Request, Response, Headers, WebSocket
  • Encoding: TextDecoder, TextEncoder, atob, btoa
  • Streams: ReadableStream, WritableStream, TransformStream
  • Crypto: crypto, CryptoKey, SubtleCrypto
  • Other: AbortController, URLPattern, structuredClone
Advanced Import Maps Configuration

Production-Ready Import Maps

{
  "imports": {
    "react": "https://esm.sh/react@18.2.0",
    "react-dom": "https://esm.sh/react-dom@18.2.0",
    "react-dom/client": "https://esm.sh/react-dom@18.2.0/client",
    "vue": "https://esm.sh/vue@3.4.8",
    "lodash": "https://esm.sh/lodash@4.17.21",
    "date-fns": "https://esm.sh/date-fns@2.30.0",
    "@company/ui-kit": "https://cdn.company.com/ui-kit@1.2.0/index.js",
    "@company/analytics": "https://cdn.company.com/analytics@2.1.0/index.js"
  },
  "scopes": {
    "/legacy/": {
      "react": "https://esm.sh/react@17.0.2",
      "react-dom": "https://esm.sh/react-dom@17.0.2"
    }
  }
}

Benefits in action:

  • πŸ“¦ Automatic Deduplication: React loaded once, shared everywhere
  • πŸš€ CDN Optimization: Load popular libraries from fast CDNs
  • πŸ“± Perfect Caching: Browser-native module caching
// In your components - just import naturally
import React from 'react'; // Shared via importmap
import { createApp } from 'vue'; // Shared via importmap
import MyComponent from '@components/MyComponent'; // Path mapping

// No build-time complexity, maximum runtime efficiency

The Web Platform Way: Instead of reinventing module sharing, we embrace the native solution that browsers are optimizing for.

πŸ”§ Advanced Features

Server Actions: Seamless Client-Server Integration

Web Widget's Server Actions feature allows you to call server-side functions directly from client components with unprecedented simplicity - no API endpoints, no fetch calls, just direct function invocation.

🎯 The Revolution

// Traditional approach: Complex API setup
// ❌ Create API endpoint
export async function POST(request: Request) {
  const data = await request.json();
  return Response.json({ message: data.content, date: new Date() });
}

// ❌ Client-side fetch calls
const handleClick = async () => {
  const response = await fetch('/api/echo', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ content: inputValue }),
  });
  const result = await response.json();
  setLog(JSON.stringify(result));
};

// βœ… Web Widget Server Actions: Pure simplicity
// Server function (functions@action.ts)
export const echo = async (content: string) => {
  return {
    message: content,
    date: new Date().toISOString(),
    respondent: 'server',
  };
};

// Client component - call server function like any local function
const handleClick = async () => {
  const result = await echo(inputValue); // Direct server call!
  setLog(JSON.stringify(result));
};

πŸš€ Key Features

  • 🎯 Direct Function Calls: Call server functions like local functions
  • πŸ“‘ Automatic Networking: Framework handles HTTP requests/responses
  • πŸ”’ Type Safety: End-to-end TypeScript support with full type checking
  • ⚑ Zero Boilerplate: No API routes, no fetch calls, no serialization code
  • 🌐 Environment Detection: Server functions automatically run server-side only

πŸ“ File Structure

routes/action/
β”œβ”€β”€ index@route.tsx        # Route page
β”œβ”€β”€ Echo@widget.tsx        # Interactive client component
β”œβ”€β”€ functions@action.ts    # Server-only functions
└── styles.css            # Styling

πŸ’» Complete Example

// functions@action.ts - Server functions
export const echo = async (content: string) => {
  // This code ONLY runs on the server
  return {
    message: content,
    date: new Date().toISOString(),
    respondent: typeof document === 'undefined' ? 'server' : 'client',
  };
};

// Echo@widget.tsx - Client component
import { useState } from 'react';
import { echo } from './functions@action';

export default function EchoWidget() {
  const [inputValue, setInputValue] = useState('');
  const [result, setResult] = useState('');

  const handleSubmit = async () => {
    // Direct server function call - no fetch, no API endpoints!
    const response = await echo(inputValue);
    setResult(JSON.stringify(response, null, 2));
  };

  return (
    <>
      <input
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
      />
      <button onClick={handleSubmit}>Send to Server</button>
      {result && <pre>{result}</pre>}
    </>
  );
}

🌟 Why Server Actions Matter

  1. 🎯 Simplicity: Write server logic as simple functions, not API endpoints
  2. ⚑ Performance: Automatic request optimization and batching
  3. πŸ”’ Security: Server functions never expose internals to client
  4. πŸ“± Developer Experience: Unified development model across client/server
  5. πŸš€ Productivity: Focus on business logic, not infrastructure code

The Future of Full-Stack Development: Server Actions represent a paradigm shift from thinking in terms of "API endpoints" to thinking in terms of "server functions" - making full-stack development as natural as writing single-tier applications.

HTTP Caching & Performance

Web Widget provides enterprise-grade HTTP caching using standard Cache Control headers:

  • Cache-Control: Standard max-age, stale-while-revalidate, stale-if-error
  • ETag & Conditional Requests: Efficient cache validation
  • Pluggable Storage: Memory, Redis, disk, or custom backends via SharedCache
// index@route.tsx

export const config = {
  cache: {
    // Cache rendered pages using HTTP cache control directives
    cacheControl: {
      // Cache for 60 seconds in shared caches
      sharedMaxAge: 60,
      // Serve stale content for 7 days on errors
      staleIfError: 604800,
      // Background revalidation for 7 days
      staleWhileRevalidate: 604800,
    },
  },
};

// ...

This mode requires integrating the @web-widget/middlewares/cache middleware

Project Setup & Configuration

Complete Project Structure

my-web-widget-app/
β”œβ”€β”€ routes/
β”‚   β”œβ”€β”€ (components)/
β”‚   β”‚   β”œβ”€β”€ BaseLayout.tsx
β”‚   β”‚   β”œβ”€β”€ Counter@widget.tsx
β”‚   β”‚   └── Counter@widget.vue
β”‚   β”œβ”€β”€ index@route.tsx
β”‚   β”œβ”€β”€ about@route.tsx
β”‚   β”œβ”€β”€ action/
β”‚   β”‚   β”œβ”€β”€ index@route.tsx
β”‚   β”‚   └── functions@action.ts
β”‚   └── api/
β”‚       └── hello@route.ts
β”œβ”€β”€ public/
β”œβ”€β”€ entry.client.ts
β”œβ”€β”€ entry.server.ts
β”œβ”€β”€ routemap.server.json
β”œβ”€β”€ importmap.client.json
β”œβ”€β”€ package.json
β”œβ”€β”€ tsconfig.json
└── vite.config.ts

Package Dependencies

{
  "dependencies": {
    "@web-widget/helpers": "^1.59.0",
    "@web-widget/html": "^1.59.0",
    "@web-widget/node": "^1.59.0",
    "@web-widget/react": "^1.59.0",
    "@web-widget/vue": "^1.59.0",
    "@web-widget/web-router": "^1.59.0",
    "@web-widget/web-widget": "^1.59.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0",
    "vue": "^3.4.8"
  },
  "devDependencies": {
    "@vitejs/plugin-react": "^4.1.1",
    "@vitejs/plugin-vue": "^5.0.0",
    "@web-widget/vite-plugin": "^1.59.0",
    "vite": "^5.4.19"
  }
}

File Description

  • routes/**/*@route.* Route modules that only run on the server side
  • routes/**/*@middleware.* Middleware that only runs on the server side
  • routes/**/*@action.* Server functions that can be called directly from client components
  • routes/**/*@widget.* Components that can interact with users, running simultaneously on both server and client sides
  • entry.client.ts Client entry point
  • entry.server.ts Server entry point
  • importmap.client.json Production module sharing configuration (used in builds only)
  • routemap.server.json Routing configuration file, automatically generated by development tools
Best Practices & Tips

Development Best Practices

  1. Technology Stack Isolation: Use widget modules to achieve isolation of different technology stack components
  2. Progressive Enhancement: Prioritize server-side rendering, add client-side interaction as needed
  3. Caching Strategy: Use lifecycle caching wisely to improve performance
  4. Error Handling: Implement comprehensive error boundaries and fallback solutions
  5. Type Safety: Make full use of TypeScript's type system

Performance Tips

  • Use renderStage="server" for static content that doesn't need interactivity
  • Use renderStage="client" for components that require browser APIs
  • Implement proper caching strategies for expensive operations
  • Keep server components lightweight to improve SSR performance

Code Organization

  • Group related routes using parentheses folders
  • Share common components through widget modules
  • Use TypeScript interfaces for prop typing
  • Implement proper error boundaries at route level

🀝 Community

Join developers exploring the future of multi-framework architecture.


🌟 The Future of Framework Freedom

Web Widget represents a new approach to frontend architecture - one that embraces technology diversity rather than fighting it. By building on Web Standards and providing elegant abstractions, we're creating a path forward that doesn't require abandoning existing investments or limiting future choices.

🎯 Why This Matters

  • βœ… Sustainable Architecture: Build applications that can evolve with the ecosystem
  • βœ… Reduced Risk: Incremental changes instead of big-bang migrations
  • βœ… Developer Choice: Use the best tool for each component, not one-size-fits-all
  • βœ… Future-Ready: Built on standards that will outlast individual frameworks

Ready to explore framework freedom?

Experience the elegant simplicity of multi-framework architecture. Build your future with Web Widget.


"Simplicity is the ultimate sophistication" - Leonardo da Vinci