With Target Observer, you can easily trigger animations, lazy load content, or handle visibility-based logic in your application.
npm install target-observeruseInView hook, InViewProvider, Target, and ObserveZone components. These tools enable seamless tracking of when specific elements, identified as "targets" enter or exit the viewport.
Target Observer, you can easily trigger animations, lazy load content, or handle visibility-based logic in your application. The library is optimized for performance and ease of use, making it a robust solution for building responsive and dynamic user interfaces.
InViewProvider component is a wrapper for tracking the visibility of multiple Target components within the viewport. The component monitors when each target enters or exits the viewport, enabling you to manage and respond to the in-view state of these elements within your application.
targetIds | - | An array of strings that specifies the IDs of the Target components to be tracked by the InViewProvider. |
firstTargetActiveOnMount | true | By default, the first target is activated when the component mounts. If set to false, it will only update once the user starts scrolling and the target enters the viewport. |
ObserveZone is an invisible component that defines a specific area within which visibility of child elements is tracked.
height | 50vh | An optional property for adjusting the observing area's height. |
className | - | An optional property for defining class names. Use only for testing the component. |
Target component represents an element whose visibility is tracked within the ObserveZone. Each Target should have a unique ID that matches an entry in the targetIds array provided to the InViewProvider.
id | - | The id of the component to track and trigger state updates. |
as | div | The element or component the Target should render as. |
entryThreshold | 0.5 | The percentage of observing zone height that should be used to trigger the observer. The target element when inside this area will update the inView property to true. Accepts value between 0 and 1. Min is 0.1 and Max is 0.9. |
tsx
import clsx from 'clsx'
import { useInView, InViewProvider, ObserveZone, Target } from 'target-observer'
const targetIds = [
'section#1',
'section#2',
'section#3',
'section#4',
'section#5'
]
export default function Example() {
return (
{/ must use position:relative in parent for ObserveZone to work /}
{/ add this invisible component to track the target /}
// optional height property, default is 50vh
height='70vh'
// optional className property, use only for testing
className='observer'
/>
{targetIds.map((targetId) => (
key={targetId}
// must specify the id property for target to work
id={targetId}
// the html element you want to render
as='section'
// add styling to your target
className='target'
// the height in percent of observing zone to trigger inView state
// default is 0.5 (50 percent)
entryThreshold={0.3}
>
Target: {targetId}
))}
)
}
function Navigation() {
const inView = useInView() // returns a record with boolean values
return (
)
}
`
`css
nav {
position: sticky;
top: 2.5rem;
height: 100vh;
}
ul {
list-style-type: none;
}
li {
width: 8rem;
text-align: center;
color: black;
}
a {
text-decoration: none;
font-size: 18px;
color: inherit;
}
li:not(:first-child):not(:last-child),
section:not(:first-child):not(:last-child) {
margin-top: 16px;
margin-bottom: 16px;
}
.list-active {
font-weight: 700;
padding: 0.25rem 0 0.25rem 0;
background-color: #ef4444;
color: white;
}
.container {
max-width: 80rem;
width: 100%;
margin-left: auto;
margin-right: auto;
padding: 1.25rem;
display: flex;
gap: 2.5rem;
}
.target-wrapper {
position: relative;
width: 100%;
}
.target {
height: 90vh;
border: 4px solid #e5e7eb;
padding: 1.25rem;
}
.observer {
outline: 4px solid rgba(239, 68, 68, 0.2);
outline-offset: 8px;
}
`
$3
`tsx
import clsx from 'clsx'
import { useInView, InViewProvider, ObserveZone, Target } from 'target-observer'
const targetIds = [
'section#1',
'section#2',
'section#3',
'section#4',
'section#5'
]
export default function Example() {
return (
{/ must use position:relative in parent for ObserveZone to work /}
{/ add this invisible component to track the target /}
// optional height property, default is 50vh
height='70vh'
// optional className property, use only for testing
className='ring-4 ring-offset-8 ring-red-500/20'
/>
{targetIds.map((targetId) => (
key={targetId}
// must specify the id property for target to work
id={targetId}
// the html element you want to render
as='section'
// add styling to your target
className='h-[90vh] border-4 p-5 w-full'
// the height in percent of observing zone to trigger inView state
// default is 0.5 (50 percent)
entryThreshold={0.3}
>
Target: {targetId}
))}
)
}
function Navigation() {
const inView = useInView() // returns a record with boolean values
return (
{targetIds.map((targetId) => (
key={targetId}
className={clsx('text-center w-32', {
'font-bold py-1 bg-red-500 text-white':
inView[targetId]
})}
>
{targetId}
))}
)
}
``