@innet/dom
v1.0.0-alpha.2
Published
Tools to build Web Site
Readme
@innet/dom
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
Index
Install • Handler • JSX • State Management • style
Components
Portal • ContextProvider • Show • Hide • For • Router • Link • Delay
Life Cycle
onDestroy • onMounted
Hooks
useParam • useParams • useParent
Install
🏠︎ / Install
To start developing an innet-dom application, use innetjs:
npx innetjs init my-app -t feReplace 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
Component params
props • children • return
Component types
Async Components • Generic Async Component • Generic Component
Default Components
Portal • ContextProvider • Show • Hide • For • Router • Link • Delay
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 nodeconst Test1 = () => 123 const Test2 = () => '123'null,undefined,boolean,symbol- ignoreconst 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 contentconst 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
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
Layout • List of Segments • Optional Segment • Permissions • Lazy Loading • Params
| 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
href • replace • class • exact • scroll • scrollTo
| 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.
