Mount Components
Neither the fast-* components supported by the custom renderer nor the components in @react-facet/dom-elements provide a way to mount or unmount children based on the value of a Facet.
For that purpose, you can use the Mount, Map, and With components. They allow you to add or remove nodes in the React tree.
Mount
Mounts its children conditionally. The mandatory prop when should be a Facet<boolean | undefined>. The component's children will be mounted when value contained within the Facet is true.
Example:
tsximport {useFacetState ,Mount } from '@react-facet/core'constExample = () => {const [displayFacet ,setDisplayFacet ] =useFacetState (true)return (<Mount when ={displayFacet }><div >Hello there</div ></Mount >)}
The Mount component is commonly used together with useFacetMap. For example, imagine that you have a search bar and a search results component. You might only want to show the search results component if the search bar has a value. That can be accomplished like so:
tsximport {useFacetState ,useFacetMap ,Mount } from '@react-facet/core'constExample = () => {const [valueFacet ,setValueFacet ] =useFacetState ('')constisValueNotNullOrEmptyFacet =useFacetMap ((value ) => {returnvalue != null &&value .length > 0},[],[valueFacet ],)return (<Mount when ={isValueNotNullOrEmptyFacet }><SomeComponent /></Mount >)}
Additionally, the Mount component takes an optional prop called condition. This will default to true. When set to false, it will mount its children when when the value contained within the Facet is false.
tsximport {useFacetState ,Mount } from '@react-facet/core'constExample = () => {const [isSignedInFacet ,setIsSignedInFacet ] =useFacetState (true)return (<><Mount when ={isSignedInFacet }><div >You are logged in!</div ></Mount ><Mount when ={isSignedInFacet }condition ={false}><div >Create an account.</div ></Mount ></>)}
Map
Mounts a list of components based on a Facet of an array.
The length of the array passed into the array prop is used to determine how many components to mount. The children of the Map components should be a function, and this function will receive a Facet containing the data and index of the current item.
The Map only re-renders if the size of the array changes, so we need to be mindful about changing the size of the array, as it will cause a performance hit. On the other hand, if only the data inside the items in the list change, but not the amount of items, there will be no re-render: the Facet passed into the children function will simply be updated with the latest data.
Example:
tsximport {useFacetState ,Map } from '@react-facet/core'constExample = () => {const [arrayFacet ,setArrayFacet ] =useFacetState (['1', '2', '3', '4', '5'])return (<Map array ={arrayFacet }>{(item ) => (<span ><fast-text text ={item } /></span >)}</Map >)}
Map accepts an optional equality check, to be performed and prevent unnecessary facet updates.
Example:
tsximport {useFacetState ,useFacetMap ,Map ,Facet ,shallowObjectEqualityCheck } from '@react-facet/core'typeInput = {value : string }constExample = () => {const [arrayFacet ,setArrayFacet ] =useFacetState <Input []>([{value : '1' },{value : '2' },{value : '3' },{value : '4' },{value : '5' },])constPrintItem = ({item ,index }: {item :Facet <Input >;index : number }) => {return (<span ><fast-text text ={useFacetMap (({value }) =>value , [], [item ])} /><span >{index }</span ></span >)}return (<Map array ={arrayFacet }equalityCheck ={shallowObjectEqualityCheck }>{(item ,index ) => <PrintItem item ={item }index ={index } />}</Map >)}
With
Mounts its children conditionally. The mandatory prop data should be a Facet of any type (Facet<T | null | undefined>). The component's children will be mounted when value contained within the Facet is not null or undefined.
This component is meant to solve an issue with refining types in TypeScript. When the data prop facet contains a value, With will call the function passed as children, passing the data Facet as an argument. The type becomes refined so that it will not be null or undefined.
Example:
tsximport {useFacetState ,With ,FacetProp ,useFacetWrap } from '@react-facet/core'typeUserDataProps = {name :FacetProp <string>middlename ?:FacetProp <string | undefined>}constUserData = ({name ,middlename }:UserDataProps ) => {constnameFacet =useFacetWrap (name )constmiddlenameFacet =useFacetWrap (middlename )return (<div ><p >Name: <fast-text text ={nameFacet } /></p ><With data ={middlenameFacet }>{(middlenameFacetWithData ) => (<p >Middlename: <fast-text text ={middlenameFacetWithData } /></p >)}</With ></div >)}
For contrast, attempting to do the same thing with Mount will not allow for correct type checking. In this scenario, the type of middlenameFacet is not refined and could still contain a null or undefined.
tsximport {useFacetWrap ,useFacetMap ,Mount ,FacetProp } from '@react-facet/core'typeUserDataProps = {name :FacetProp <string>middlename ?:FacetProp <string | undefined>}constUserData = ({name ,middlename }:UserDataProps ) => {constnameFacet =useFacetWrap (name )constmiddlenameFacet =useFacetWrap (middlename )return (<div ><p >Name: <fast-text text ={nameFacet } /></p ><Mount when ={useFacetMap ((middlename ) =>middlename != null, [], [middlenameFacet ])}><p >Middlename: <fast-text text ={useFacetMap ((middlename ) =>middlename || '', [], [middlenameFacet ])} /></p >{/* Since TypeScript cannot know that `middlenameFacet` now holds a `string` for sure, it will still believe thatit could be `string | undefined`. The only way to solve this issue is to extract a new component or use a typeassertion. Neither is a good option, hence we recommend using `With` instead in this scenario. */}</Mount ></div >)}
Unwrap
The Unwrap component extracts the plain value from a Facet and renders children with that unwrapped value. Use it when you need to pass a plain value into JSX (for example to a third-party component), or when you want to limit the scope of a React re-render to a small subtree.
Important: Unwrap uses useFacetUnwrap internally, which creates React state for the unwrapped value. However, Unwrap typically produces a smaller re-render scope than calling useFacetUnwrap at the top level of a component because it confines the stateful re-render to its children.
Signature:
tsx<Unwrap data={someFacet}>{(value) => <SomeChild value={value} />}</Unwrap>
Basic example:
tsximport {useFacetState ,Unwrap } from '@react-facet/core'constExample = () => {const [nameFacet ] =useFacetState <string | undefined>('Alex')return <Unwrap data ={nameFacet }>{(name ) => <div >Hello, {name }!</div >}</Unwrap >}
When to use:
- Interfacing with third-party components that accept plain values (not facets).
- Local rendering scenarios where a small, controlled re-render is acceptable.
- Prefer
Unwrapover callinguseFacetUnwrapat the top level of your component:Unwraplimits the re-render to its children instead of making the entire component re-render on facet updates.
Prefer Unwrap over multiple Mount components when you need to choose between mutually-exclusive branches. For example, instead of creating two Mounts for opposite conditions:
tsximport {useFacetState ,useFacetMap ,Mount } from '@react-facet/core'constFoo = () => <div >Foo</div >constBar = () => <div >Bar</div >constBad = () => {const [condFacet ] =useFacetState <boolean | undefined>(true)return (<><Mount when ={useFacetMap ((c ) => !!c , [], [condFacet ])}><Foo /></Mount ><Mount when ={useFacetMap ((c ) => !c , [], [condFacet ])}><Bar /></Mount ></>)}
Use a single Unwrap that renders one branch or the other:
tsximport {useFacetState ,Unwrap } from '@react-facet/core'constFoo = () => <div >Foo</div >constBar = () => <div >Bar</div >constGood = () => {const [condFacet ] =useFacetState <boolean | undefined>(true)return <Unwrap data ={condFacet }>{(cond ) => (cond ? <Foo /> : <Bar />)}</Unwrap >}// Note: Foo and Bar are declared in the previous example for brevity and re-use in this file.
Why this matters:
- Each
Mountinternally manages mounting state; using twoMounts for opposite conditions can create more internal React state and may result in multiple re-renders for the same logical switch. A singleUnwrapkeeps the state and re-render scope smaller. - When opposing conditions change at slightly different times, multiple
Mounts can briefly both mount or unmount, causing transient visual glitches. A singleUnwrapevaluates the condition once and consistently renders the chosen branch.
When NOT to use:
- For binding dynamic values to the DOM — prefer
fast-*components or facet-aware patterns instead. - For simple single-condition mounts where
MountorWithare the clearer, more efficient choice. UseUnwrapwhen you need a concise multi-branch conditional rendered from a single facet.
See also:
- useFacetUnwrap - The hook used internally (performance warning applies)
Times
Renders a child-producing function a fixed number of times based on a Facet<number>.
The Times component accepts a single required prop, count, which must be a Facet<number>. Its children prop is a function that will be called for each index from 0 to count - 1 and should return a ReactElement or null.
This component is useful when you want to repeat a piece of UI a dynamic number of times while keeping updates scoped to the repeated subtree.
Signature:
tsx<Times count={countFacet}>{(index, count) => <Item key={index} index={index} total={count} />}</Times>
Basic example:
tsximport {useFacetState ,Times ,NO_VALUE } from '@react-facet/core'constExample = () => {const [countFacet ,setCount ] =useFacetState (3)return (<div ><Times count ={countFacet }>{(index ) => <div key ={index }>Row {index }</div >}</Times ><button onClick ={() =>setCount ((c ) => (c !==NO_VALUE ?c + 1 : 1))}>Add</button ></div >)}
Performance considerations
Timessubscribes to the providedcountfacet. When the numeric value changes,Timeswill re-evaluate how many children to render.- If the
countincreases or decreases, the component mounts or unmounts the corresponding child subtrees, which can be more expensive than updating existing children. Prefer keeping the array size stable when possible (e.g., reuse items) to avoid mounting churn. - The child function receives the
indexand the currentcountas plain values. If you need facet-aware children, useMapwith an array facet instead.
When to use
- Use
Timeswhen you need to repeat a UI fragment a variable number of times determined by a numeric facet. - Use
Mapinstead when you have an array of data and want each item wrapped in aFacet(for per-item updates without remounting the whole list when data changes but length stays the same).