npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@innet/dom

v1.0.0-alpha.2

Published

Tools to build Web Site

Readme

  @innet/dom

 

NPM downloads tests changelog license

Overview

@innet/dom is a lightweight and reactive frontend framework built on top of innet. It provides a simple, JSX-based API for building highly performant web applications with fine-grained reactivity and minimal boilerplate.

Why Use @innet/dom?

@innet/dom offers a declarative and reactive approach to UI development without the overhead of virtual DOM diffing. It enables developers to write simple, composable components with direct DOM manipulation, resulting in faster rendering and smaller bundle sizes compared to traditional frameworks.

Key features include:

  • JSX support with full TypeScript integration
  • Fine-grained reactivity powered by watch-state
  • Built-in routing with flexible route definitions and permissions
  • Support for async components and code splitting
  • Lifecycle hooks for mounting and cleanup
  • Context API for dependency injection and state sharing
  • Utility components like Portal, Show, Hide, and Delay for common UI patterns
  • Seamless integration with CSS Modules and styling utilities

stars watchers

Index

InstallHandlerJSXState Managementstyle

Components
PortalContextProviderShowHideForRouterLinkDelay

Life Cycle
onDestroyonMounted

Hooks
useParamuseParamsuseParent

Utils
RefContext

Install

🏠︎ / Install

To start developing an innet-dom application, use innetjs:

npx innetjs init my-app -t fe

Replace my-app with your working folder name

Go into the my-app directory and check README.md

Handler

🏠︎ / Handler

Use the dom handler to run the application.

Clear the src folder and create index.ts inside.

import innet from 'innet'
import dom from '@innet/dom'

import app from './app'

innet(app, dom)

JSX

🏠︎ / JSX

You can use XML-like syntax (JSX) to create and insert elements into the DOM. Learn more about JSX here.

Create app.tsx in src folder.

export default (
  <h1>
    Hello World!
  </h1>
)

Everything you provide as the first argument to the innet function (with the dom handler) will be rendered inside the body element.

Portal

🏠︎ / Components / Portal

| Prop | Type | Description | |----------|----------------------------------------|-----------------------------------------------------------| | parent * | TargetElements | DocumentFragment | The element where the child content will be rendered | | children | JSX.Element | The content to render inside the specified parent element |

If you want to render content into an element other than body, use the Portal component.

For example, you can modify index.html in public folder.

<!doctype html>
<html lang="en">
<head ... >
<body>
  <div id="app"></div>
  <!-- add this ^ -->
</body>
</html>

And modify app.tsx

import { Portal } from '@innet/dom'

const app = document.getElementById('app')

export default (
  <Portal parent={app}>
    <h1>
      Hello World!
    </h1>
  </Portal>
)

You can use Portal anywhere inside your app.

Modify app.tsx

import { Portal } from '@innet/dom'

const app = document.getElementById('app')
const myElement = document.createElement('div')

export default (
  <Portal parent={app}>
    <h1>
      Hello World!
    </h1>
    <Portal parent={myElement}>
      This is content of myElement
    </Portal>
  </Portal>
)

myElement should contain This is content of myElement and app should contain the following code.

<h1>
  Hello World!
</h1>

State Management

🏠︎ / State Management

With innet, you can avoid the traditional component-based approach while still having access to state management.

State management is powered by watch-state

To bind state to content, use State, Cache, or a function as the content.

Turn back index.html and modify app.tsx

import { State } from 'watch-state'

const count = new State(0)

const increase = () => {
  count.value++
}

export default (
  <>
    <h1>
      Count: {count}
    </h1>
    <button onclick={increase}>
      Click Me
    </button>
  </>
)

To bind a state and a prop use State, Cache or a function as a value of the prop.

Modify app.tsx

import { State } from 'watch-state'

const darkMode = new State(false)

const handleChange = (e: Event) => {
  darkMode.value = e.target.checked
}

export default (
  <div class={() => darkMode.value ? 'dark' : 'light'}>
    <h1>
      Hello World!
    </h1>
    <label>
      <input
        type="checkbox"
        onchange={handleChange}
      />
      Dark Mode
    </label>
  </div>
)

Components

🏠︎ / Components

Life Cycle

Component params
propschildrenreturn

Component types
Async ComponentsGeneric Async ComponentGeneric Component

Default Components
PortalContextProviderShowHideForRouterLinkDelay

A component is a function. You can use it as a JSX element.

Create Content.tsx

export const Content = () => (
  <h1>
    Hello World!
  </h1>
)

Modify app.tsx

import { Content } from './Content'

export default (
  <Content />
)

Props

🏠︎ / Components / Props

Each component receives a single argument: an object containing its props.

Modify Content.tsx

export function Content ({ color }) {
  return (
    <h1 style={{ color }}>
      Hello World!
    </h1>
  )
}

You should pass the color prop when using the component.

Modify app.tsx

import { Content } from './Content'

export default (
  <Content color='red' />
)

Children

🏠︎ / Components / Children

Component props can include a children prop.

Modify Content.tsx

export function Content ({ children }) {
  return <h1>{children}</h1>
}

You can pass children as content inside the component.

Modify app.tsx

import { Content } from './Content'

export default (
  <Content>Content</Content>
)

Return

🏠︎ / Components / Return

A component can return:

  • string, number - render as text node
    const Test1 = () => 123
    const Test2 = () => '123'
  • null, undefined, boolean, symbol - ignore
    const Test1 = () => null
    const Test2 = () => {}
    const Test3 = () => true
    const Test4 = () => Symbol()
  • DOM Element - put in the DOM
    const Test = () => document.createElement('div')
  • JSX Fragment, array - render content
    const Test1 = () => <>content</>
    const Test2 = () => ['content']
  • JSX Element - put in the DOM
    const Test1 = () => <div>content</div>
    const Test2 = () => <br />
  • function - observable children
    const state = new State()
    const Test1 = () => () => state.value
    const Test2 = () => state
    const Test3 = () => <>{() => state.value}</>

Async Component

🏠︎ / Components / Async Component

Innet supports async components, you can simplify previous code.

async function Content () {
  const { text } = await fetch('...').then(e => e.json())

  return <div>{text}</div>
}

innetjs helps to make code splitting.

async function Content () {
  const { Test } = await import('./Test')

  return (
    <div>
      <Test />
    </div>
  )
}

Test.tsx

export const Test = () => (
  <div>
    Test success!
  </div>
)

While it's loading nothing can be shown. If you want to show something, use Generic Async Component.

Generic Async Component

🏠︎ / Components / Generic Async Component

Simply add an asterisk and use yield instead of return.

async function * Content () {
  yield 'Loading...'

  const { text } = await fetch('...').then(e => e.json())

  yield <div>{text}</div>
}

Generic Component

🏠︎ / Components / Generic Component

It can be useful when you want to do something after a content deployed.

function * Content () {
  yield (
    <div id='test'>
      Hello World!
    </div>
  )

  console.log(document.getElementById('test'))
}

You can use queueMicrotask instead of a generic component, but there are a small difference:

queueMicrotask runs after whole content is available and generic component runs right after the content of the component rendered.

function * A () {
  queueMicrotask(() => {
    console.log(
      'queueMicrotask A',
      document.getElementById('a'),
      document.getElementById('b'),
    )
  })

  yield <span id='a' />

  console.log(
    'generic A',
    document.getElementById('a'),
    document.getElementById('b'),
  )
}

function * B () {
  queueMicrotask(() => {
    console.log(
      'queueMicrotask B',
      document.getElementById('a'),
      document.getElementById('b'),
    )
  })

  yield <span id='b' />

  console.log(
    'generic B',
    document.getElementById('a'),
    document.getElementById('b'),
  )
}

function Content () {
  return (
    <>
      <A />
      <B />
    </>
  )
}

You get the next output:

generic A <span id="a"></span> null
generic B <span id="a"></span> <span id="b"></span>
queueMicrotask A <span id="a"></span> <span id="b"></span>
queueMicrotask B <span id="a"></span> <span id="b"></span>

Life Cycle

🏠︎ / Components / Life Cycle

onDestroyonMounted

Each component renders only once.

There are three lifecycle stages:

  • render (DOM elements are not created)
  • mounted (DOM elements are created)
  • destroy (elements will be removed from the DOM)

Because of a component renders only once you can have effects right inside the component function.

import { State } from 'watch-state'

function Content () {
  const state = new State()

  fetch('...')
    .then(e => e.json())
    .then(data => {
      state.value = data.text
    })

  return (
    <div>
      {state}
    </div>
  )
}

onDestroy

🏠︎ / Components / Life Cycle / onDestroy

You can subscribe on destroy of a component by onDestroy from watch-state

Modify Content.tsx

import { State, onDestroy } from 'watch-state'

export function Content() {
  const count = new State(0)
  // create a state

  const timer = setInterval(() => {
    count.value++
  }, 1000)
  // increase the state each second

  onDestroy(() => clearInterval(timer))
  // stop timer on destroy

  return () => count.value
  // return observable value
}

And modify app.tsx

import { State } from 'watch-state'
import { Show } from '@innet/dom'
import { Content } from './Content'

const show = new State(true)

const handleChange = (e: Event) => {
  show.value = e.target.checked
}

export default (
  <>
    <Show when={show}>
      <Content />
    </Show>
    <input
      type="checkbox"
      checked
      onchange={handleChange}
    />
  </>
)

onMounted

🏠︎ / Components / Life Cycle / onMounted

You can use onMounted to do something after end of rendering.

Modify Content.tsx

import { onMounted, Ref } from '@innet/dom'
import { State, onDestroy } from 'watch-state'

export function Content() {
  const width = new State(0)
  const ref = new Ref<HTMLDivElement>()

  onMounted(() => {
    console.log(ref.value.clientWidth)
  })

  return <div ref={ref}>Hello world</div>
}

Ref

🏠︎ / Ref

Ref helps to get an HTML element.

import { Ref } from '@innet/dom'

function * Content () {
  const wrapper = new Ref<HTMLDivElement>()
  
  yield (
    <div ref={wrapper}>
      Hello World!
    </div>
  )

  console.log(wrapper.value)
}

Context

🏠︎ / Context

You can pass a value from a parent element through any children to the place you need.

Modify Content.tsx

import { Context, useContext } from '@innet/dom'

export const color = new Context('blue')

export function Content () {
  const currentColor = useContext(color)

  return (
    <h1 style={{ color: currentColor }}>
      {children}
    </h1>
  )
}

ContextProvider

🏠︎ / Components / ContextProvider

| Prop | Type | Description | |------------|--------------------------------|-----------------------------------------------------------------| | for * | Context<T> | Context<T>[] | A context or array of contexts to provide | | set * | T | T[] | A value or array of values to pass to the context(s) | | children * | JSX.Element | Child elements that will have access to the provided context(s) |

Use ContextProvider from @innet/jsx to provide context value into children.

Modify app.tsx

import { ContextProvider } from '@innet/jsx'
import { Content, color } from './Content'

export default (
  <>
    <Content>
      Without context
    </Content>
    <ContextProvider for={color} set='red'>
      <Content>
        With context
      </Content>
    </ContextProvider>
  </>
)

Show

🏠︎ / Components / Show

| Prop | Type | Description | |----------|----------------------------------------------|-----------------------------------------------------| | when * | State<T> | Cache<T> | () => T | T | Condition to determine whether to show the children | | fallback | JSX.Element | Element to render if the condition is not met | | children | JSX.Element | Content to render when the condition is met |

You can use Show component to show/hide content by state.

import { Show } from '@innet/dom'
import { State } from 'watch-state'

const show = new State(true)

export default (
  <Show when={show}>
    <button
      onclick={() => {
        show.value = false
      }}>
      Click Me
    </button>
  </Show>
)

Hide

🏠︎ / Components / Hide

| Prop | Type | Description | |----------|----------------------------------------------|-----------------------------------------------------| | when * | State<T> | Cache<T> | () => T | T | Condition to determine whether to hide the children | | fallback | JSX.Element | Element to render if the condition is met | | children | JSX.Element | Content to render when the condition is not met |

You can use Hide component to show/hide content by state.

import { Hide } from '@innet/dom'
import { State } from 'watch-state'

const isHidden = new State(false)

export default (
  <Hide when={isHidden}>
    <button
      onclick={() => {
        isHidden.value = true
      }}>
      Click Me
    </button>
  </Hide>
)

For

🏠︎ / Components / For

| Prop | Type | Description | |----------|---------------------------------------------------------|-----------------------------------------------------------| | of * | StateProp<Iterable<T>> | The collection to iterate over | | key | keyof T | (item: T) => any | Unique key for each item, used for DOM optimization | | children | (item: State<T>, index: State<number>) => JSX.Element | Function that returns JSX for each item in the collection |

You can use map method of an array to put view on data.

const names = ['Mike', 'Alex', 'Dan']

export default (
  <ul>
    {names.map(name => (
      <li>
        {name}
      </li>
    ))}
  </ul>
)

It's ok for static data, but if you use a state, it's better to use For component.

import { For } from '@innet/dom'
import { State } from 'watch-state'

const names = new State(['Mike', 'Alex', 'Dan'])

export default (
  <ul>
    <For of={names}>
      {(name, index) => (
        <li>
          #{index}:
          {name}
        </li>
      )}
    </For>
  </ul>
)

Use key property to improve DOM changes when you use an array of objects with some uniq field, like id.

import { For } from '@innet/dom'
import { State } from 'watch-state'

const users = new State([
  { id: 1, text: 'test1' },
  { id: 2, text: 'test2' },
  { id: 3, text: 'test3' },
])

export default (
  <ul>
    <For of={users} key='id'>
      {(user, index) => (
        <li>
          #{index}:
          {() => user.value.name}
        </li>
      )}
    </For>
  </ul>
)

Router

🏠︎ / Components / Router

LayoutList of SegmentsOptional SegmentPermissionsLazy LoadingParams

Hooks
useParamuseParams

| Prop | Type | Description | |-------------|--------------------------|------------------------------------------------------| | routing * | StateProp<Routing> | Routing object that defines the route structure | | permissions | StateProp<Set<string>> | Set of permissions required to access certain routes |

You can render content based on the current URL.

Use component to specify the component for a route. Use path to define URL path segments. Use index to mark a route as the endpoint for a given path.

import { Router, createRouting } from '@innet/dom'

const routing = createRouting([
  { index: true, component: () => 'Home page' },
  {
    index: true,
    path: 'settings',
    component: () => 'Settings Index',
  },
  {
    path: 'settings',
    component: () => 'Settings Rest',
  },
  { component: () => 'Not Found' }
])

export const Content = () => (
  <Router routing={routing} />
)

The following routes will be available:

/ - Home page
/settings - Settings Index
/settings/foo - Settings Rest /foo - Not Found

You can split path segments using /

import { Router, createRouting } from '@innet/dom'

const routing = createRouting([
  { index: true, component: () => 'Home page' },
  {
    index: true,
    path: 'settings',
    component: () => 'Settings Index',
  },
  {
    index: true,
    path: 'settings/account',
    component: () => 'Account Settings',
  },
  {
    index: true,
    path: 'settings/notifications',
    component: () => 'Notification Settings',
  },
  { component: () => 'Not Found' }
])

export const Content = () => (
  <Router routing={routing} />
)

The following routes will be available:

/ - Home page
/settings - Settings Index
/settings/account - Account Settings
/settings/notifications - Notification Settings
/settings/foo - Not Found
/foo - Not Found

Layout

🏠︎ / Components / Router / Layout

You can group routes using children. The component field on a group defines a layout for its child pages.

import { Router, createRouting, ChildrenProps } from '@innet/dom'

const Home = () => 'Home Page'
const About = () => 'About Page'
const Settings = () => 'Settings Page'
const NotFound = () => 'NotFound Page'

const MainLayout = (props: ChildrenProps) => <article>{props.children}</article>
const SecondLayout = (props: ChildrenProps) => <div>{props.children}</div>

const routing = createRouting([
  {
    component: MainLayout,
    children: [
      { index: true, component: Home },
      { index: true, path: 'about', component: About },
      { component: NotFound },
    ],
  },
  {
    component: SecondLayout,
    children: [
      { index: true, path: 'settings', component: Settings },
    ],
  },
])

export const Content = () => (
  <Router routing={routing} />
)

The following routes will be available:

/ - <article>Home Page</article>
/about - <article>About Page</article>
/settings - <div>Settings Page</div>
/settings/foo - <article>NotFound Page</article>
/foo - <article>NotFound Page</article>

List of Segments

🏠︎ / Components / Router / List of Segments

You can separate available segments with |.

import { Router, createRouting } from '@innet/dom'

const Home = () => 'Home Page'
const FooBar = () => 'FooBar Page'
const NotFound = () => 'NotFound Page'

const routing = createRouting([
  { index: true, component: Home },
  { path: 'foo|bar', component: FooBar },
  { component: NotFound },
])

export const Content = () => (
  <Router routing={routing} />
)

/ - Home page
/foo - FooBar Page
/bar - FooBar Page
/baz - NotFound Page

Optional Segment

🏠︎ / Components / Router / Optional Segment

You can add ? at the end of a segment to make it optional.

import { Router, createRouting, ChildrenProps } from '@innet/dom'

const Home = () => 'Home Page'
const Settings = ({ children }: ChildrenProps) => <div>{children}</div>
const MainTab = () => 'Main Tab'
const AccountTab = () => 'Account Tab'
const NotificationsTab = () => 'Notifications Tab'
const NotFound = () => 'NotFound Page'

const routing = createRouting([
  { index: true, component: Home },
  { path: 'settings', component: Settings, children: [
    { index: true, path: 'main?', component: MainTab },
    { index: true, path: 'account', component: AccountTab },
    { index: true, path: 'notifications', component: NotificationsTab },
  ]},
  { component: NotFound },
])

export const Content = () => (
  <Router routing={routing} />
)

/ - Home page
/settings - <div>Main Tab</div>
/settings/main - <div>Main Tab</div>
/settings/account - <div>Account Tab</div>
/settings/notifications - <div>Notifications Tab</div>
/settings/foo - NotFound Page
/foo - NotFound Page

Permissions

🏠︎ / Components / Router / Permissions
import { Router, createRouting } from '@innet/dom'
import { State } from 'watch-state'

const Home = () => 'Home Page'
const Settings = () => 'Settings Page'
const NotFound = () => 'NotFound Page'

const permissions = new State(new Set<string>())

const routing = createRouting([
  { index: true, component: Home },
  {
    path: 'settings',
    permissions: ['postlogin'],
    component: Settings,
  },
  { component: NotFound },
])

export const Content = () => (
  <Router routing={routing} permissions={permissions} />
)

/ - Home page
/settings - NotFound Page
/foo - NotFound Page

Set permissions

permissions.value.add('postlogin')
permissions.update()

/ - Home page
/settings - Settings Page
/foo - NotFound Page

Lazy Loading

🏠︎ / Components / Router / Lazy Loading

You can use lazy to load pages and layouts asynchronously, enabling code-splitting by pages and layouts.

You can use fallback field to render a glimmer while pages or layouts are loading. You can use childrenFallback field to set fallback for children elements.

import { Router, createRouting, lazy } from '@innet/dom'

const routing = createRouting([
  {
    childrenFallback: 'Loading...',
    children: [
      {
        index: true,
        component: lazy(() => import('./Home')),
        fallback: 'Home Loading...',
      },
      {
        path: 'settings',
        component: lazy(() => import('./Settings')),
      },
      { component: lazy(() => import('./NotFound')) },
    ],
  }
])

export const Content = () => (
  <Router routing={routing} />
)

Params

🏠︎ / Components / Router / Params

Prefix a path segment with : to capture its value as a param.

import { Router, createRouting, useParam } from '@innet/dom'

const Home = () => 'Home Page'
const Products = () => 'Products Page'
const NotFound = () => 'NotFound Page'

const Product = () => {
  const productId = useParam('productId')

  return <>Product: {productId}</>
}

const routing = createRouting([
  {
    index: true,
    component: Home,
  },
  {
    path: 'products',
    children: [
      {
        index: true,
        component: Products,
      },
      {
        path: ':productId',
        component: Product,
      },
    ],
  },
  { component: NotFound },
])

export const Content = () => (
  <Router routing={routing} />
)

/ - Home page
/products - Products Page
/products/123 - Product: 123
/foo - NotFound Page

useParam

🏠︎ / Components / Router / useParam

You can get a route param by useParam.

import { Router, createRouting, useParam } from '@innet/dom'

const UserPage = () => {
  const userId = useParam('userId')
  
  return <div>{userId}</div>
}

const routing = createRouting([
  { index: true, component: () => 'Home page' },
  {
    index: true,
    path: 'user/:userId',
    component: UserPage,
  },
  { component: () => 'Not Found' }
])

export const Content = () => (
  <Router routing={routing}/>
)

/ - Home page
/user/123 - <div>123</div>
/user - Not Found

You can use square brackets and | to specify allowed values for a param. You can use ? to set optional param.

import { Router, createRouting, useParam } from '@innet/dom'

const Home = () => {
  const lang = useParam('lang')
  
  return <>Home: {lang}</>
}

const About = () => {
  const lang = useParam('lang')
  
  return <>About: {lang}</>
}

const NotFound = () => 'NotFound Page'

const routing = createRouting([
  {
    path: ':lang[en|ru]?',
    children: [
      { index: true, component: Home },
      { index: true, path: 'about', component: About },
    ]
  },
  { component: NotFound },
])

export const Content = () => (
  <Router routing={routing} />
)

/ - Home:
/en - Home: en
/ru - Home: ru
/about - About:
/en/about - About: en
/ru/about - About: ru
/de/about - Not Found
/de - Not Found

useParams

🏠︎ / Components / Router / useParams

You can get all route params by useParams.

import { Router, createRouting, ChildrenProps, useParams } from '@innet/dom'

const UserPage = (props: ChildrenProps) => {
  const params = useParams()
  
  return <div>{() => params.value.userId}</div>
}

const routing = createRouting([
  {index: true, component: () => 'Home page'},
  {
    index: true,
    path: 'user/:userId',
    component: UserPage,
  },
  {component: () => 'Not Found'}
])

export const Content = () => (
  <Router routing={routing}/>
)

/ - Home page
/user/123 - <div>123</div>
/user - Not Found

Link

🏠︎ / Components / Link

hrefreplaceclassexactscrollscrollTo

| Prop | Type | Description | |----------|------------------------------------------------------|----------------------------------------------------------------| | href | string | URL or path the link navigates to | | target | '_blank' | '_parent' | '__self' | '__top' | The target attribute for the link | | scroll | 'after' | 'before' | 'none' | Controls scroll behavior on navigation | | scrollTo | number | string | Position or selector to scroll to after navigation | | replace | boolean | Replace the current history entry instead of pushing a new one | | exact | boolean | Match the path exactly instead of by prefix | | class | string | { root: string, active: string } | CSS class(es) for the link and its active state | | children | JSX.Element | Content to render inside the link |

Use the Link component to create links. It behaves like an HTML <a> tag but uses the History API for internal navigation. For external links, it automatically adds rel="noopener noreferrer nofollow" and target="_blank" attributes.

href

🏠︎ / Components / Link / href

If href starts from /, ? or # then the Link will use History API.

import { Link, Router, createRouting } from '@innet/dom'

const routing = createRouting([
  { index: true, component: () => 'Home Page' },
  { index: true, path: 'test', component: () => 'Test Page' },
  { component: () => '404' },
])

export const Content = () => (
  <div>
    <Link href="/">home</Link>
    <Link href="/test">test</Link>
    <Link href="/home">unknown</Link>
    <div>
      <Router routing={routing} />
    </div>
  </div>
)

replace

🏠︎ / Components / Link / replace

By default, it pushes to history, but you may use replace to replace current history state.

<Link replace href="/">
  home
</Link>

class

🏠︎ / Components / Link / class

You can add root or active link class

import { Link } from '@innet/dom'

const classes = {
  root: 'link',
  active: 'active',
}

export const Content = () => (
  <div>
    <Link href='/' class='only-root'>
      home
    </Link>
    <Link href='/test' class={classes}>
      test
    </Link>
  </div>
)

You can use all features from html-classes for the class prop.

import { Link } from '@innet/dom'

const classes = {
  root: ['link1', 'link2', () => 'dynamic-class'],
  active: { active: true },
}

export const Content = () => (
  <div>
    <Link href='/' class={() => 'dynamic-root'}>
      home
    </Link>
    <Link href='/test' class={classes}>
      test
    </Link>
  </div>
)

exact

🏠︎ / Components / Link / exact

By default, active class appends if URL starts with href prop value, but use exact to compare exactly.

import { Link } from '@innet/dom'

const classes = { root: 'link', active: 'active' }

export const Content = () => (
  <div>
    <Link href='/' exact classes={classes}>
      home
    </Link>
    <Link href="/test" classes={classes}>
      test
    </Link>
  </div>
)

scroll

🏠︎ / Components / Link / scroll

You can use smooth scroll

body, html {
  scroll-behavior: smooth;
}

The property of scroll says should we scroll on click and how.

by default equals before

import { Link } from '@innet/dom'

export const Content = () => (
  <div>
    <Link href="/" scroll='before'>
      home
    </Link>
    <Link href="/test" scroll='after'>
      test
    </Link>
    <Link href="?modal" scroll='none'>
      test
    </Link>
  </div>
)

scrollTo

🏠︎ / Components / Link / scrollTo

If you want to scroll the page to custom position (by default it's up of the page) use scrollTo

import { Link } from '@innet/dom'

export const Content = () => (
  <div>
    <Link href='/' scrollTo={100}>
      home
    </Link>
    <Link href='/test' scrollTo='#root'>
      test
    </Link>
  </div>
)

Use a string to scroll under an element relates to the CSS selector you provide or use -1 to stop scrolling.

Delay

🏠︎ / Components / Delay

| Prop | Type | Description | |----------|-----------------------|----------------------------------------------------| | show | number | Delay before showing the content (in milliseconds) | | hide | number | Delay before hiding the content (in milliseconds) | | ref | Ref<State<boolean>> | Reference to the visibility state | | children | JSX.Element | Content to render with delay |

You can show or hide elements with a delay.

import { Delay } from '@innet/dom'

export function Content () {
  return (
    <Delay show={1000}>
      Works
      <Delay show={1000}>
        fine!
      </Delay>
    </Delay>
  )
}

useHidden

🏠︎ / Components / Delay / useHidden

You can react to elements being removed.

Modify Content.tsx

import { useHidden } from '@innet/dom'

export function Content () {
  const hidden = useHidden()

  return () => hidden.value ? 'hidden' : 'shown'
}

And modify app.tsx

import { Delay } from '@innet/dom'
import { State } from 'watch-state'

const show = new State(true)

const handleClick = () => {
  show.value = false
}

export default () => show.value && (
  <Delay hide={1000}>
    <Content />
    <button onclick={handleClick}>
      Hide
    </button>
  </Delay>
)

ref

🏠︎ / Components / Delay / ref

You can use ref to access the hidden state.

Modify Content.tsx

import { Delay } from '@innet/dom'

export function Content () {
  const hidden = new Ref()

  return (
    <Delay ref={hidden} hide={1000}>
      {() => hidden.value.value ? 'hidden' : 'shown'}
    </Delay>
  )
}

And modify app.tsx

import { State } from 'watch-state'

const show = new State(true)

const handleClick = () => {
  show.value = false
}

export default () => show.value && (
  <>
    <Content />
    <button onclick={handleClick}>
      Hide
    </button>
  </>
)

Hooks

🏠︎ / Hooks

You can use hooks only inside a component.

export async function Content (props1) {
  const value1 = useHook1()
  const value2 = useHook2()
}

useProps

🏠︎ / Hooks / useProps

You can get props with useProps hook.

import { useProps } from '@innet/jsx'

export function Content (props1) {
  const props2 = useProps()

  return (
    <h1>
      {props1 === props2 ? 'same' : 'different'}
    </h1>
  )
}

useChildren

🏠︎ / Hooks / useChildren

To get children elements you can take useChildren.

Change Content.tsx

import { useChildren } from '@innet/jsx'

export function Content ({ color, children }) {
  return (
    <h1 style={{ color }}>
      {String(children === useChildren())}
    </h1>
  )
}

Then you can use the children outside.

Modify app.tsx

import { Content } from './Content'

export default (
  <Content color='red'>
    Hello World!
  </Content>
)

useParent

🏠︎ / Hooks / useParent

You can get parent HTML element inside a component

import { getParent } from '@innet/dom'

export function Content () {
  console.log(useParent())
}

style

🏠︎ / style

You can style components with style function. The function returns useStyle hook. Use this hook inside a component to get html-classes features on class prop.

import { style, Style } from '@innet/dom'

import styles from './Content.scss'
// or you can use an object like
// { root: '...', header: '...', content: '...' }

const useContentStyles = style(styles)

export interface ContentProps extends Style {}

export function Content (props: ContentProps) {
  const styles = useContentStyles()

  return (
    <div class={() => styles.root}>
      <header class={() => styles.header}>
        header
      </header>
      <main class={() => styles.content}>
        content
      </main>
    </div>
  )
}

Then you can use class prop to define classes.

import { State } from 'watch-state'

const show = new State(true)

const handleClick = () => {
  show.value = !show.value
}

export default (
  <>
    <Content
      class={{
        root: 'root',
        header: ['header', 'another-class'],
        content: [
          'content',
          () => show.value && 'show'
        ],
      }}
    />
    <button
      onclick={handleClick}>
      Hide
    </button>
  </>
)

Issues

If you find a bug or have a suggestion, please file an issue on GitHub.

issues