SkillAgentSearch skills...

Myra

Myra is a simple and small Typescript framework for building web interfaces.

Install / Use

/learn @jhdrn/Myra
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Myra

Myra is (another) JSX rendering library. It is small, simple and built with and for TypeScript.

npm CircleCI codecov Downloads gzip size install size

NPM

Myra implements a React-like API (hooks, memo, fragments) on top of a custom virtual DOM diffing engine. It has no runtime dependencies.

Setup

Install with npm:

npm install --save myra

Add a tsconfig.json to your project:

{
  "compilerOptions": {
    "target": "es2015",
    "module": "es2015",
    "jsx": "react",
    "jsxFactory": "myra.h",
    "jsxFragmentFactory": "myra.Fragment",

    /* Optional, but recommended */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true
  }
}

Quick start

import * as myra from 'myra'

const Counter = myra.define(() => {
    const [count, setCount] = myra.useState(0)

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

myra.mount(<Counter />, document.body)

Mounting a component

Use myra.mount to mount a component to the DOM:

myra.mount(<MyComponent />, document.body)

Defining components

Use myra.define to wrap a component function. This is purely a convenience for TypeScript type inference and has no runtime effect:

interface Props {
    name: string
}

const MyComponent = myra.define<Props>(({ name }) => <p>Hello, {name}!</p>)

Hooks

useState

Manages local component state. Supports lazy initialization:

const [count, setCount] = myra.useState(0)
const [data, setData] = myra.useState(() => expensiveInitialValue())

// Functional update
setCount(prev => prev + 1)

useEffect / useLayoutEffect

useEffect runs asynchronously after render. useLayoutEffect runs synchronously after render (equivalent to React's useLayoutEffect). Both accept an optional deps array.

myra.useEffect(() => {
    const sub = subscribe()
    return () => sub.unsubscribe() // optional cleanup
}, [dep])

useRef

const inputRef = myra.useRef<HTMLInputElement>()

return <input ref={inputRef} />
// inputRef.current is the DOM element after render

useMemo

Memoizes a computed value. Re-computes when deps change:

const sorted = myra.useMemo(() => items.slice().sort(), [items])

useCallback

Memoizes a callback. Re-creates when deps change:

const handleClick = myra.useCallback(() => setCount(c => c + 1), [])

useReducer

An alternative to useState for more complex state logic. Works the same as React's useReducer:

type Action = { type: 'increment' } | { type: 'decrement' }

function reducer(state: number, action: Action): number {
    switch (action.type) {
        case 'increment': return state + 1
        case 'decrement': return state - 1
    }
}

const Counter = myra.define(() => {
    const [count, dispatch] = myra.useReducer(reducer, 0)

    return (
        <div>
            <p>{count}</p>
            <button onclick={() => dispatch({ type: 'increment' })}>+</button>
            <button onclick={() => dispatch({ type: 'decrement' })}>-</button>
        </div>
    )
})

useContext

Subscribes to a context value provided by a Context.Provider ancestor. Re-renders the component whenever the context value changes. Falls back to the default value if no matching provider is found in the tree:

const ThemeContext = myra.createContext('light')

const ThemedButton = myra.define(() => {
    const theme = myra.useContext(ThemeContext)
    return <button class={theme}>Click me</button>
})

useErrorHandler

Catches errors thrown during render and shows a fallback view:

myra.useErrorHandler(error => <p>An error occurred: {error}</p>)

Context

Context lets you pass values down the component tree without threading props through every level.

Use myra.createContext to create a context object with a default value, then wrap the subtree with its Provider to supply a value:

const ThemeContext = myra.createContext('light')

const App = myra.define(() => (
    <ThemeContext.Provider value="dark">
        <ThemedButton />
    </ThemeContext.Provider>
))

Any descendant can read the nearest provider's value with useContext (see above). When the provider's value prop changes, all subscribed consumers re-render automatically. If no provider is found, the default value passed to createContext is used.

Memoized components

Use myra.memo to skip re-renders when props have not changed. By default a shallow comparison is used. Pass a custom comparator as the second argument to override:

const MyMemoComponent = myra.memo<Props>(props => <p>{props.name}</p>)

// Custom comparator — return true to keep the existing render, false to re-render
const MyMemoComponent = myra.memo<Props>(
    props => <p>{props.name}</p>,
    (newProps, oldProps) => newProps.name === oldProps.name
)

Fragments

Use <></> (or <myra.Fragment>) to return multiple elements without a wrapper:

const MyComponent = myra.define(() => (
    <>
        <h1>Title</h1>
        <p>Body</p>
    </>
))

Special props

  • key — ensures stable identity for list items during reconciliation. Must be unique among siblings. Also prevents unnecessary re-renders of elements.
  • class — maps to the DOM className property.
  • ref — populated with the DOM element after render (use with useRef).
  • nothing — a special JSX tag that always renders as an HTML comment node (<!-- Nothing -->), useful as a conditional placeholder.

License

MIT

View on GitHub
GitHub Stars22
CategoryDevelopment
Updated3d ago
Forks1

Languages

TypeScript

Security Score

95/100

Audited on Mar 29, 2026

No findings