# Flexium

[![npm version](https://img.shields.io/npm/v/flexium.svg)](https://www.npmjs.com/package/flexium)
[![npm downloads](https://img.shields.io/npm/dm/flexium.svg)](https://www.npmjs.com/package/flexium)
[![license](https://img.shields.io/npm/l/flexium.svg)](https://github.com/Wick-Lim/flexium.js/blob/main/LICENSE)

**Simpler, Faster, Unified.**

Flexium is a next-generation UI framework that unifies state management, async data fetching, and global state into a single, powerful API: `use()`.

## Key Features

- **Unified State API** - No more `useRecoil`, `useQuery` separation. Just `use()`.
- **No Virtual DOM** - Direct DOM updates via Proxy-based fine-grained reactivity.
- **Tiny Bundle** - Minimal footprint with tree-shaking support.
- **Cross-Platform** - DOM and Server (SSR) renderers included.
- **TypeScript First** - Full type inference out of the box.
- **Zero-Config JSX** - Works with standard tooling.

## Installation

```bash
npm install flexium
```

## Quick Start

```bash
npm create flexium@latest my-app
cd my-app
npm install
npm run dev
```

## unsafeEffect cleanup behavior

As of v0.18, `unsafeEffect` called inside a component render is automatically tied to the component's lifecycle and gets `.stop()`-ed on unmount.

| API | Auto cleanup | When to use |
|---|---|---|
| `unsafeEffect(fn)` | Yes (if inside a component) | Most cases — derived state, side effects scoped to component |
| `unsafeEffect.detached(fn)` | No | Framework renderers, root effects, intentional global subscriptions |

## Global state cleanup

Global signals (`use(value, { key: ['foo'] })`) are reference-counted. When the last
component reading the key unmounts, the registry entry is freed automatically.

For explicit invalidation, use `releaseGlobalKey(serializedKey)`:

```ts
import { releaseGlobalKey } from 'flexium/core'

// On user logout, clear all user-scoped global state
releaseGlobalKey(JSON.stringify(['user', userId]))
```

## The Only API You Need: `use()`

Flexium unifies all state concepts into one function.

### Local State

```tsx
import { use } from 'flexium/core'
import { render } from 'flexium/dom'

function Counter() {
  const [count, setCount] = use(0)

  return (
    <button onclick={() => setCount(c => c + 1)}>
      Count: {count}
    </button>
  )
}

render(Counter, document.getElementById('app'))
```

### Global State

Just add a `key` to share state across components. Keys can be strings or arrays.

```tsx
// Define global state with array key
const [theme, setTheme] = use('light', { key: ['app', 'theme'] })

function ThemeToggle() {
  // Access same state anywhere with the same key
  const [theme, setTheme] = use('light', { key: ['app', 'theme'] })

  return (
    <button onclick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
      Theme: {theme}
    </button>
  )
}
```

### Async Resources

Pass an async function to handle data fetching automatically.

```tsx
function UserProfile({ id }) {
  const [user, control] = use(async () => {
    const res = await fetch(`/api/users/${id}`)
    return res.json()
  })

  if (control.loading) return <div>Loading...</div>
  if (control.error) return <div>Error!</div>

  return (
    <div>
      <h1>{user.name}</h1>
      <button onclick={() => control.refetch()}>Reload</button>
    </div>
  )
}
```

### Computed/Derived State

```tsx
const [count, setCount] = use(1)
const [doubled] = use(() => count * 2, [count])
```

## Package Structure

```
flexium
├── /core         # Core reactivity: use(), sync(), useRef(), Context, Useable
├── /dom          # DOM renderer: render(), hydrate(), Portal, Suspense
├── /router       # SPA routing: Routes, Route, Link, Outlet, useRouter(), useLocation()
└── /server       # SSR: renderToString(), renderToStaticMarkup()
```

## Control Flow

Use native JavaScript for control flow - no special components needed:

```tsx
// Conditional rendering
{isLoggedIn ? <Dashboard /> : <Login />}

// Short-circuit for simple conditions
{isAdmin && <AdminPanel />}

// List rendering
{items.map(item => <Item key={item.id} data={item} />)}
```

## Routing

```tsx
import { Routes, Route, Link, useRouter } from 'flexium/router'

function App() {
  return (
    <Routes>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/about">About</Link>
      </nav>
      <Route path="/" component={Home} />
      <Route path="/about" component={About} />
      <Route path="/users/:id" component={UserProfile} />
    </Routes>
  )
}

function UserProfile({ params }) {
  return <h1>User: {params.id}</h1>
}

// Or use the router hook
function UserProfileHook() {
  const r = useRouter()
  return <h1>User: {r.params.id}</h1>
}
```

## Server-Side Rendering

```tsx
import { renderToString } from 'flexium/server'
import { hydrate } from 'flexium/dom'

// Server
const { html, state } = renderToString(App, { hydrate: true })

// Client
hydrate(App, document.getElementById('root'), { state })
```

## Built-in Components

### Portal

```tsx
import { Portal } from 'flexium/dom'

<Portal target={document.body}>
  <Modal />
</Portal>
```

### Suspense

```tsx
import { Suspense, lazy } from 'flexium/dom'

const LazyComponent = lazy(() => import('./Heavy'))

<Suspense fallback={<Loading />}>
  <LazyComponent />
</Suspense>
```

### ErrorBoundary

```tsx
import { ErrorBoundary } from 'flexium/dom'

<ErrorBoundary fallback={(error) => <Error message={error.message} />}>
  <App />
</ErrorBoundary>
```

## Context API

```tsx
import { use, Context } from 'flexium/core'

// Create context with default value
const ThemeCtx = new Context<'light' | 'dark'>('light')

function App() {
  const [theme, setTheme] = use<'light' | 'dark'>('light')

  return (
    <ThemeCtx.Provider value={theme}>
      <button onclick={() => setTheme(t => t === 'light' ? 'dark' : 'light')}>
        Toggle Theme
      </button>
      <Child />
    </ThemeCtx.Provider>
  )
}

function Child() {
  const [theme] = use(ThemeCtx)
  return <div class={theme}>Current theme: {theme}</div>
}
```

## Ecosystem

| Package | Description |
|---------|-------------|
| [flexium-ui](https://www.npmjs.com/package/flexium-ui) | Column/Row based UI component library |
| [flexium-canvas](https://www.npmjs.com/package/flexium-canvas) | Canvas, WebGL, and interactive modules |
| [create-flexium](https://www.npmjs.com/package/create-flexium) | Scaffold a Flexium app |

## Documentation

Full documentation available at [https://flexium.junhyuk.im](https://flexium.junhyuk.im)

## License

MIT
