Convert Figma logo to code with AI

Tencent logoomi

Web Components Framework - Web组件框架

13,048
1,241
13,048
70

Top Related Projects

7,994

🐰 Rax is a progressive framework for building universal application. https://rax.js.org

227,213

The library for web and native user interfaces.

207,677

This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core

95,657

Deliver web apps with confidence 🚀

78,194

Cybernetically enhanced web apps

36,546

⚛️ Fast 3kB React alternative with the same modern API. Components & Virtual DOM.

Quick Overview

Omi is a front-end framework for building user interfaces. It combines the best features of React, Web Components, and Proxy, offering a powerful and efficient way to create modern web applications. Omi allows developers to write components once and use them everywhere, including custom elements, React, Vue, and more.

Pros

  • Cross-framework compatibility: Components can be used in various frameworks
  • Web Components support: Native browser support for better performance
  • Virtual DOM and JSX: Familiar syntax for React developers
  • Small size: Lightweight core library for faster loading times

Cons

  • Smaller community compared to React or Vue
  • Less extensive ecosystem of third-party components
  • Steeper learning curve for developers new to Web Components
  • Limited documentation and resources compared to more established frameworks

Code Examples

Creating a simple component:

import { define, WeElement, render } from 'omi'

define('my-counter', class extends WeElement {
  data = {
    count: 0
  }

  static css = `
    span { color: red; }
  `

  render(props, data) {
    return (
      <div>
        <button onClick={() => this.data.count--}>-</button>
        <span>{data.count}</span>
        <button onClick={() => this.data.count++}>+</button>
      </div>
    )
  }
})

render(<my-counter />, 'body')

Using Omi with TypeScript:

import { define, Component, h, render } from 'omi'

interface MyComponentProps {
  name: string
}

define('my-component', class extends Component<MyComponentProps> {
  render() {
    return <h1>Hello {this.props.name}!</h1>
  }
})

render(<my-component name="Omi" />, 'body')

Creating a functional component:

import { define, render } from 'omi'

const HelloWorld = ({ name }) => <h1>Hello {name}!</h1>

define('hello-world', HelloWorld)

render(<hello-world name="Omi" />, 'body')

Getting Started

To start using Omi, follow these steps:

  1. Install Omi using npm:

    npm install omi
    
  2. Create a new Omi project using the CLI:

    npx omi-cli init my-app
    cd my-app
    npm start
    
  3. Start building your components in the src directory. The main entry point is typically src/index.js or src/index.tsx for TypeScript projects.

  4. Use the define function to create custom elements and the render function to mount your components to the DOM.

Competitor Comparisons

7,994

🐰 Rax is a progressive framework for building universal application. https://rax.js.org

Pros of Rax

  • More mature and widely adopted, with a larger ecosystem and community support
  • Better performance optimization for mobile devices and cross-platform development
  • Extensive documentation and learning resources available

Cons of Rax

  • Steeper learning curve, especially for developers new to React-like frameworks
  • Less flexible than Omi in terms of component definition and state management
  • Primarily focused on Alibaba's ecosystem, which may limit its applicability in some projects

Code Comparison

Rax component:

import { createElement, Component } from 'rax';
import View from 'rax-view';
import Text from 'rax-text';

class HelloWorld extends Component {
  render() {
    return (
      <View>
        <Text>Hello, World!</Text>
      </View>
    );
  }
}

Omi component:

import { define, WeElement, render } from 'omi'

define('hello-world', class extends WeElement {
  render() {
    return (
      <div>
        <h1>Hello, World!</h1>
      </div>
    )
  }
})

render(<hello-world />, 'body')

Both frameworks offer component-based development, but Omi uses Web Components under the hood, while Rax follows a more React-like approach. Rax requires separate imports for basic elements like View and Text, whereas Omi allows direct use of HTML tags. Omi's custom element definition style may be more intuitive for developers familiar with Web Components.

227,213

The library for web and native user interfaces.

Pros of React

  • Larger ecosystem and community support
  • More extensive documentation and learning resources
  • Wider adoption in industry, leading to better job prospects

Cons of React

  • Steeper learning curve for beginners
  • Requires additional libraries for state management and routing
  • Larger bundle size compared to Omi

Code Comparison

React:

import React from 'react';

function App() {
  return <h1>Hello, World!</h1>;
}

Omi:

import { define, WeElement, render } from 'omi';

define('my-app', class extends WeElement {
  render() {
    return <h1>Hello, World!</h1>;
  }
});

Summary

React offers a more mature ecosystem and widespread adoption, making it a popular choice for large-scale applications. Omi, on the other hand, provides a simpler API and smaller bundle size, which can be advantageous for lightweight projects. While React has a steeper learning curve, it offers more extensive documentation and community support. Omi's syntax is similar to React but with some unique features, potentially making it easier for developers familiar with Web Components.

207,677

This is the repo for Vue 2. For Vue 3, go to https://github.com/vuejs/core

Pros of Vue

  • Larger ecosystem and community support
  • More mature and battle-tested in production environments
  • Extensive documentation and learning resources

Cons of Vue

  • Steeper learning curve for beginners
  • Larger bundle size compared to Omi
  • More complex state management for large applications

Code Comparison

Vue component:

<template>
  <div>{{ message }}</div>
</template>

<script>
export default {
  data() {
    return {
      message: 'Hello, Vue!'
    }
  }
}
</script>

Omi component:

import { define, WeElement } from 'omi'

define('my-component', class extends WeElement {
  render() {
    return <div>{this.data.message}</div>
  }
})

Vue and Omi are both JavaScript frameworks for building user interfaces, but they have different approaches and features. Vue is more widely adopted and has a larger ecosystem, making it easier to find solutions and third-party libraries. However, Omi offers a simpler learning curve and smaller bundle size, which can be advantageous for certain projects.

Vue's template syntax is more verbose but provides clear separation of concerns. Omi uses JSX, which may be more familiar to React developers. Both frameworks support component-based architecture, but Vue's reactivity system is more sophisticated, while Omi focuses on simplicity and performance.

95,657

Deliver web apps with confidence 🚀

Pros of Angular

  • More mature and widely adopted framework with a larger ecosystem
  • Comprehensive documentation and extensive community support
  • Built-in dependency injection system for better modularity

Cons of Angular

  • Steeper learning curve due to its complexity
  • Larger bundle size, which can impact initial load times
  • More opinionated, potentially limiting flexibility in some cases

Code Comparison

Angular component:

@Component({
  selector: 'app-example',
  template: '<h1>{{ title }}</h1>'
})
export class ExampleComponent {
  title = 'Hello, Angular!';
}

Omi component:

define('my-element', class extends WeElement {
  render() {
    return html`<h1>${this.props.title}</h1>`
  }
})

Angular uses TypeScript and a decorator-based approach, while Omi employs a more lightweight, JavaScript-based syntax. Angular's component structure is more verbose but provides stronger typing and clearer separation of concerns. Omi's syntax is more concise and may be easier for developers familiar with web components.

Both frameworks offer component-based architecture, but Angular provides a more comprehensive solution with additional features like routing and form handling built-in. Omi, being lighter, may be more suitable for smaller projects or when integrating with existing systems.

78,194

Cybernetically enhanced web apps

Pros of Svelte

  • Smaller bundle sizes due to compile-time optimization
  • Simpler, more intuitive syntax with less boilerplate
  • Built-in state management without additional libraries

Cons of Svelte

  • Smaller ecosystem and community compared to Omi
  • Less flexibility for complex applications
  • Limited tooling and IDE support

Code Comparison

Svelte component:

<script>
  let count = 0;
  function increment() {
    count += 1;
  }
</script>

<button on:click={increment}>
  Clicks: {count}
</button>

Omi component:

import { define, WeElement } from 'omi'

define('my-counter', class extends WeElement {
  data = { count: 0 }
  increment = () => {
    this.data.count++
    this.update()
  }
  render() {
    return (
      <button onClick={this.increment}>
        Clicks: {this.data.count}
      </button>
    )
  }
})

The Svelte example demonstrates its concise syntax and built-in reactivity, while the Omi example showcases its similarity to React-like component structures. Svelte's approach results in less code and a more straightforward implementation, but Omi's structure may be more familiar to developers coming from other frameworks.

36,546

⚛️ Fast 3kB React alternative with the same modern API. Components & Virtual DOM.

Pros of Preact

  • Smaller bundle size (3KB gzipped) compared to Omi's larger footprint
  • Faster performance due to its lightweight nature and optimized virtual DOM
  • Wider adoption and community support, with more resources and third-party libraries available

Cons of Preact

  • Less feature-rich out of the box, requiring additional libraries for complex applications
  • Limited native TypeScript support, whereas Omi has built-in TypeScript integration
  • Steeper learning curve for developers not familiar with React-like syntax

Code Comparison

Preact:

import { h, render } from 'preact';

const App = () => <h1>Hello, World!</h1>;

render(<App />, document.body);

Omi:

import { define, WeElement, render } from 'omi';

define('my-app', class extends WeElement {
  render() {
    return <h1>Hello, World!</h1>;
  }
});

render(<my-app />, 'body');

Both frameworks offer a similar component-based approach, but Omi uses a class-based structure with custom elements, while Preact follows a more React-like functional component style. Preact's syntax is generally more concise, which aligns with its lightweight philosophy.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

English | 简体中文

omi

Omi - Web Components Framework

import { render, signal, tag, Component, h } from 'omi'

const count = signal(0)

function add() {
  count.value++
}

function sub() {
  count.value--
}

@tag('counter-demo')
export class CounterDemo extends Component {
  static css = 'span { color: red; }'

  render() {
    return (
      <>
        <button onClick={sub}>-</button>
        <span>{count.value}</span>
        <button onClick={add}>+</button>
      </>
    )
  }
}

Use this component:

import { h } from 'omi'
import './counter-demo'

render(<counter-demo />, document.body)

// or 
import { CounterDemo, Other } from './counter-demo'
// Prevent tree Shaking when importing other things
render(<CounterDemo />, document.body)

// or
document.body.appendChild(document.createElement('counter-demo'))

Install

npm i omi

To quickly create an Omi + Vite + TS/JS project:

$ npx omi-cli init my-app    # or create js project by: npx omi-cli init-js my-app
$ cd my-app           
$ npm start           # develop
$ npm run build       # release

To quickly create an Omi + Router + Signal + Suspense + Tailwindcss + Vite + TS project:

$ npx omi-cli init-spa my-app  
$ cd my-app           
$ npm start           # develop
$ npm run build       # release

Packages

  • Core packages
    • omi - Implementation of omi framework.
    • omi-form - Powerful, simple and cross frameworks form solution.
    • lucide-omi - Lucide icon collection for omi.
    • omiu - Hope to create the best web components. For example, the powerful vchart and vtable
    • omi-router - Create SPA of omi framework.
    • omi-cli - To quickly create an Omi + Vite + TS/JS project.
  • Starter kits (not published to npm)
    • omi-elements - Tailwind Element Omi UI KIT.
    • omi-starter-spa - A starter repo for building single page app using Omi + OmiRouter + Tailwindcss + TypeScript + Vite + Prettier.
    • omi-starter-ts - A starter repo for building web app or reusable components using Omi in TypeScript base on Vite.
    • omi-starter-tailwind - A starter repo for building web app or reusable components using Omi + Tailwindcss + TypeScript + Vite.
    • omi-starter-js - A starter repo for building web app or reusable components using Omi in JavaScript base on Vite.
    • omi-vue - Vue SFC + Vite + OMI + OMI-WeUI.
  • Components
  • Directives
    • omi-transition - Applying animations when an component is entering and leaving the DOM.
    • omi-ripple - A lightweight component for adding ripple effects to user interface elements.
  • Examples (not published to npm)

If you want to help the project grow, start by simply sharing it with your peers!

Thank you!

Usage

TodoApp with reactivity functions

Data oriented programming

In data-oriented programming, the focus is on the data itself and the operations on the data, rather than the objects or data structures that hold the data. This programming paradigm emphasizes the change and flow of data, and how to respond to these changes. The TodoApp with reactivity functions is a good example of this, using the concepts of reactive programming, where the UI automatically updates to reflect changes in the data (i.e., the to-do list).

import { render, signal, computed, tag, Component, h } from 'omi'

const todos = signal([
  { text: 'Learn OMI', completed: true },
  { text: 'Learn Web Components', completed: false },
  { text: 'Learn JSX', completed: false },
  { text: 'Learn Signal', completed: false }
])

const completedCount = computed(() => {
  return todos.value.filter(todo => todo.completed).length
})

const newItem = signal('')

function addTodo() {
  // api a
  todos.value.push({ text: newItem.value, completed: false })
  todos.update() // Trigger UI auto update
  
  // api b, same as api a
  // todos.value = [...todos.value, { text: newItem.value, completed: false }]

  newItem.value = '' // Changing the value type can automatically update the UI
}

function removeTodo(index: number) {
  todos.value.splice(index, 1)
  todos.update() // Trigger UI auto update
}

@tag('todo-list')
class TodoList extends Component {
  onInput = (event: Event) => {
    const target = event.target as HTMLInputElement
    newItem.value = target.value
  }

  render() {
    return (
      <>
        <input type="text" value={newItem.value} onInput={this.onInput} />
        <button onClick={addTodo}>Add</button>
        <ul>
          {todos.value.map((todo, index) => {
            return (
              <li>
                <label>
                  <input
                    type="checkbox"
                    checked={todo.completed}
                    onInput={() => {
                      todo.completed = !todo.completed
                      todos.update() // Trigger UI auto update
                    }}
                  />
                  {todo.completed ? <s>{todo.text}</s> : todo.text}
                </label>
                {' '}
                <button onClick={() => removeTodo(index)}>❌</button>
              </li>
            )
          })}
        </ul>
        <p>Completed count: {completedCount.value}</p>
      </>
    )
  }
}

render(<todo-list />, document.body)

TodoApp with Signal Class

Object oriented programming

In object-oriented programming, the focus is on the objects, which contain both data and methods to operate on the data. This programming paradigm emphasizes the interaction and cooperation between objects, and how to organize and manage code through object encapsulation, inheritance, and polymorphism. The TodoApp with reactivity functions can also be implemented in an object-oriented way, for example, by creating a TodoList class that contains the data of the to-do list and methods to operate on this data, as well as a update method to update the UI.

import { render, Signal, tag, Component, h, computed } from 'omi'

type Todo = { text: string, completed: boolean }

class TodoApp extends Signal<{ todos: Todo[], filter: string, newItem: string }> {
  completedCount: ReturnType<typeof computed>

  constructor(todos: Todo[] = []) {
    super({ todos, filter: 'all', newItem: '' })
    this.completedCount = computed(() => this.value.todos.filter(todo => todo.completed).length)
  }

  addTodo = () => {
    // api a
    this.value.todos.push({ text: this.value.newItem, completed: false })
    this.value.newItem = ''
    this.update()

    // api b, same as api a
    // this.update((value) => {
    //   value.todos.push({ text: value.newItem, completed: false })
    //   value.newItem = ''
    // })
  }

  toggleTodo = (index: number) => {
    const todo = this.value.todos[index]
    todo.completed = !todo.completed
    this.update()
  }

  removeTodo = (index: number) => {
    this.value.todos.splice(index, 1)
    this.update()
  }
}

const todoApp = new TodoApp([
  { text: 'Learn OMI', completed: true },
  { text: 'Learn Web Components', completed: false },
  { text: 'Learn JSX', completed: false },
  { text: 'Learn Signal', completed: false }
])

@tag('todo-list')
class TodoList extends Component {
  onInput = (event: Event) => {
    const target = event.target as HTMLInputElement
    todoApp.value.newItem = target.value
  }

  render() {
    const { todos } = todoApp.value
    const { completedCount, toggleTodo, addTodo, removeTodo } = todoApp
    return (
      <>
        <input type="text" value={todoApp.value.newItem} onInput={this.onInput} />
        <button onClick={addTodo}>Add</button>
        <ul>
          {todos.map((todo, index) => {
            return (
              <li>
                <label>
                  <input
                    type="checkbox"
                    checked={todo.completed}
                    onInput={() => toggleTodo(index)}
                  />
                  {todo.completed ? <s>{todo.text}</s> : todo.text}
                </label>
                {' '}
                <button onClick={() => removeTodo(index)}>❌</button>
              </li>
            )
          })}
        </ul>
        <p>Completed count: {completedCount.value}</p>
      </>
    )
  }
}

render(<todo-list />, document.body)

We won't discuss which method is good or bad here. You can choose either method using omi.

Auto Import h

vite.config.js:

import { defineConfig } from 'vite'

export default defineConfig({
  esbuild: {
    jsxInject: "import { h } from 'omi'",
    jsxFactory: "h",
    jsxFragment: "h.f"
  }
})

You can inject code during construction, so you don't have to manually export h.

Define Cross Framework Component

The case of using Omi component in Vue is as follows:

my-counter.tsx:

import { tag, Component, h, bind } from 'omi'

@tag('my-counter')
class MyCounter extends Component {
  static props = {
    count: {
      type: Number,
      default: 0,
      changed(newValue, oldValue) {
        this.state.count = newValue
        this.update()
      }
    }
  }

  state = {
    count: null
  }

  install() {
    this.state.count = this.props.count
  }

  @bind
  sub() {
    this.state.count--
    this.update()
    this.fire('change', this.state.count)
  }

  @bind
  add() {
    this.state.count++
    this.update()
    this.fire('change', this.state.count)
  }

  render() {
    return (
      <>
        <button onClick={this.sub}>-</button>
        <span>{this.state.count}</span>
        <button onClick={this.add}>+</button>
      </>
    )
  }
}

Using in Vue3

<script setup>
import { ref } from 'vue'
// import omi component
import './my-counter'

defineProps({
  msg: String,
})

const count = ref(0)

const change = (e) => {
  count.value = e.detail
}

</script>

<template>
  <h1>{{ msg }}</h1>

  <my-counter @change="change" :count="count" />
  <p>
    【Omi】 
  </p>

  <div class="card">
    <button type="button" @click="count++">count is {{ count }}</button>
    <p>
     【Vue】 
    </p>
  </div>

</template>

If you fire the count-change in an Omi component:

this.fire('count-change', this.state.count)

To use the component and listen for events in Vue:

<my-counter @count-change="change" :count="count" />

Using in React

import { useState, useRef, useEffect } from 'react'
import useEventListener from '@use-it/event-listener'
import './my-counter'

function App() {
  const [count, setCount] = useState(100)
  const myCounterRef = useRef(null)

  useEffect(() => {
    const counter = myCounterRef.current
    if (counter) {
      const handleChange = (evt) => {
        setCount(evt.detail)
      }
      counter.addEventListener('change', handleChange)
      return () => {
        counter.removeEventListener('change', handleChange)
      }
    }
  }, [])

  return (
    <>
      <h1>Omi + React</h1>
      <my-counter count={count} ref={myCounterRef}></my-counter>
      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
      </div>
    </>
  )
}

export default App

Contributors

License

MIT © Tencent

NPM DownloadsLast 30 Days