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 🙏

© 2026 – Pkg Stats / Ryan Hefner

react-view-import

v0.2.1

Published

Lazy import React components only when they enter the viewport — optimized for Next.js static exports.

Readme

🚀 react-view-import

React View Import is a lightweight utility for lazy-importing entire modules with flexible loading strategies — ideal for performance-critical apps and static Next.js (output: 'export') sites.

By default, it loads modules immediately when components are in the viewport (often on mount), but also supports delayed loading on custom conditions.

It helps you:

  • ⚡ Lazy import modules by default when components enter the viewport (loads immediately if already visible on mount)
  • 🎯 Force load on mount for critical components that need immediate availability regardless of visibility
  • 🎛️ Load on condition using boolean flags for custom loading logic (click, hover, sequential loading, etc.)
  • 🧱 Keep your bundle lean by deferring heavy component code
  • 🚀 Improve first load performance and bandwidth usage
  • 🧭 Avoid unnecessary useEffect overhead
  • 🪄 Work seamlessly with static exports and SSR-disabled pages

✨ Features

  • ✅ Lazy module imports - Import entire modules only when components enter the viewport
  • ✅ Flexible loading strategies - Load on viewport visibility, mount, or custom boolean conditions
  • ✅ Advanced loading patterns - Sequential loading, dependency-based loading, interaction-based loading
  • ✅ Performance optimization - Configurable thresholds, root margins, and loading strategies
  • ✅ Error handling - Graceful failure handling and custom loading states
  • ✅ Framework integration - Works seamlessly with Next.js, Vite, CRA, and more
  • ✅ Bundle size optimization - Reduce initial bundle size by deferring heavy component code
  • ✅ Works with both Next.js and CRA/Vite
  • ✅ Minimal runtime (~1 KB gzipped)
  • ✅ No dependency on useEffect or delayed hydration
  • ✅ Compatible with output: 'export' builds

📦 Installation

npm install react-view-import
# or
yarn add react-view-import
# or
pnpm add react-view-import

🚀 Usage

Basic Example

import { UILazyInView } from "react-view-import";

const HeavyComponent = () => <div>This component loads only when visible!</div>;
export default HeavyComponent;

export default function App() {
  return (
    <UILazyInView
      importer={() => import("./HeavyComponent")}
      select={(module) => module.default}
      componentProps={{}}
    />
  );
}

With Named Export

import { UILazyInView } from "react-view-import";

export const MyComponent = ({ title }: { title: string }) => <div>{title}</div>;

export default function App() {
  return (
    <UILazyInView
      importer={() => import("./components/MyComponent")}
      select={(module) => module.MyComponent}
      componentProps={{ title: "Hello" }}
    />
  );
}

Backward Compatibility

The select API is backward-compatible. Existing code using exportName continues to work without changes:

<UILazyInView
  importer={() => import("./PhoneField")}
  exportName="PhoneField"
  componentProps={phoneFieldProps}
/>

You can migrate to the type-safe selector when convenient:

<UILazyInView
  importer={() => import("./PhoneField")}
  select={(module) => module.PhoneField}
  componentProps={phoneFieldProps}
/>

Both forms use the same lazy dynamic import behavior. select is recommended for new code because TypeScript can validate the selected export and editors can recognize it as a real symbol reference.

Use either select or exportName, not both. No immediate migration is required for existing consumers.

With Custom Placeholder and Options

import { UILazyInView } from "react-view-import";

export default function App() {
  return (
    <UILazyInView
      importer={() => import("./HeavyChart")}
      exportName="HeavyChart"
      componentProps={{ data: chartData }}
      placeholder={<div>Loading chart...</div>}
      threshold={0.1}
      rootMargin="50px"
      onInView={() => console.log("Component is now visible")}
    />
  );
}

Load on Mount (Forced)

Load the component immediately when the parent component mounts, regardless of viewport visibility:

import { UILazyInView } from "react-view-import";

export default function App() {
  return (
    <UILazyInView
      importer={() => import("./CriticalComponent")}
      exportName="CriticalComponent"
      componentProps={{}}
      loadOnMount={true}
    />
  );
}

Load on Custom Condition

Load the component based on a custom boolean condition instead of viewport visibility:

import { UILazyInView } from "react-view-import";

export default function App() {
  const [shouldLoad, setShouldLoad] = useState(false);

  return (
    <div>
      <button onClick={() => setShouldLoad(true)}>Load Component</button>
      <UILazyInView
        importer={() => import("./ConditionalComponent")}
        exportName="ConditionalComponent"
        componentProps={{}}
        loadOnCondition={shouldLoad}
      />
    </div>
  );
}

Advanced: Sequential Loading on Scroll

Load components one after another as you scroll, or based on user interactions like hover/click:

import { useState, useCallback } from "react";
import { UILazyInView } from "react-view-import";

interface SectionData {
  title: string;
  index: number;
  importer: () => Promise<{ default: React.ComponentType<any> }>;
}

export const SequentialLoader = ({ sections }: { sections: SectionData[] }) => {
  const [loadedIndexes, setLoadedIndexes] = useState<number[]>([]);

  const handleInView = useCallback((index: number) => {
    // Mark section as visible when it enters viewport
    setLoadedIndexes((prev) =>
      prev.includes(index) ? prev : [...prev, index],
    );
  }, []);

  return (
    <div>
      {sections.map(({ title, index, importer }) => (
        <UILazyInView
          key={index}
          importer={importer}
          exportName="default"
          componentProps={{ title }}
          loadOnCondition={loadedIndexes.includes(index)}
          onInView={() => handleInView(index)}
          placeholder={<div>Loading {title}...</div>}
        />
      ))}
    </div>
  );
};

This enables patterns like:

  • Sequential loading: Load components one by one as user scrolls
  • Dependency-based loading: Load component only after previous components are loaded
  • Interaction-based loading: Load on click, hover, or other user interactions
  • Progressive enhancement: Load additional content based on user engagement

🔧 Advanced Usage

Performance Optimization

Root Margin Tuning

Control when loading triggers with rootMargin:

// Load 2 seconds before entering viewport (good for slow networks)
<UILazyInView
  importer={() => import("./HeavyComponent")}
  exportName="default"
  componentProps={{}}
  rootMargin="2000px 0px"  // 2 seconds at 60fps scroll
/>

// Load only when component is fully visible
<UILazyInView
  importer={() => import("./Component")}
  exportName="default"
  componentProps={{}}
  threshold={1.0}  // 100% visible
/>

Bundle Splitting Strategy

Optimize bundle splitting by component size:

// Large components - preload earlier
<UILazyInView
  importer={() => import("./DataVisualization")}
  exportName="Chart"
  componentProps={{}}
  rootMargin="1000px 0px"
/>

// Small components - load when visible
<UILazyInView
  importer={() => import("./SmallWidget")}
  exportName="default"
  componentProps={{}}
  rootMargin="100px 0px"
/>

Error Handling

Handle import failures gracefully:

import { useState } from "react";
import { UILazyInView } from "react-view-import";

const ErrorBoundary = ({ children, fallback }) => {
  const [hasError, setHasError] = useState(false);

  if (hasError) return fallback;

  return <div onError={() => setHasError(true)}>{children}</div>;
};

export default function App() {
  return (
    <ErrorBoundary fallback={<div>Failed to load component</div>}>
      <UILazyInView
        importer={() => import("./UnstableComponent")}
        exportName="default"
        componentProps={{}}
        placeholder={<div>Loading...</div>}
      />
    </ErrorBoundary>
  );
}

Loading States Management

Create custom loading states:

import { useState } from "react";
import { UILazyInView } from "react-view-import";

const LoadingStates = {
  IDLE: "idle",
  LOADING: "loading",
  LOADED: "loaded",
  ERROR: "error",
};

export const SmartLoader = ({ importer, exportName, componentProps }) => {
  const [loadState, setLoadState] = useState(LoadingStates.IDLE);

  const handleLoadStart = () => setLoadState(LoadingStates.LOADING);
  const handleLoadComplete = () => setLoadState(LoadingStates.LOADED);
  const handleLoadError = () => setLoadState(LoadingStates.ERROR);

  return (
    <UILazyInView
      importer={async () => {
        handleLoadStart();
        try {
          const module = await importer();
          handleLoadComplete();
          return module;
        } catch (error) {
          handleLoadError();
          throw error;
        }
      }}
      exportName={exportName}
      componentProps={componentProps}
      placeholder={
        loadState === LoadingStates.LOADING ? (
          <div>🚀 Loading...</div>
        ) : (
          <div>⏳ Preparing...</div>
        )
      }
    />
  );
};

Framework Integration

Next.js App Router

// app/components/LazySection.tsx
"use client";

import { UILazyInView } from "react-view-import";

// Dynamic imports work with both default and named exports
export const LazySection = ({ sectionId }) => (
  <UILazyInView
    importer={() => import(`../sections/${sectionId}`)}
    exportName="default" // Use "default" for default exports
    componentProps={{}}
    rootMargin="500px 0px"
  />
);

Selecting an Export

Use select to return the component from the imported module. The selector is type-safe, creates a static reference that editors can track, and works with function, arrow, and class components:

// For default exports:
export default MyComponent;
<UILazyInView
  importer={() => import("./MyComponent")}
  select={(module) => module.default}
  componentProps={{}}
/>;

// For named exports:
export const MyComponent = () => <div />;
<UILazyInView
  importer={() => import("./MyComponent")}
  select={(module) => module.MyComponent}
  componentProps={{}}
/>;

The existing exportName="MyComponent" form remains supported for backward compatibility, but it cannot detect misspelled or unused exports during static analysis.

Vite with Dynamic Imports

// Optimize chunk naming
const loadChart = () =>
  import(/* webpackChunkName: "charts" */ "./ChartComponent");

<UILazyInView importer={loadChart} exportName="default" componentProps={{}} />;

With React Suspense

import { Suspense } from "react";
import { UILazyInView } from "react-view-import";

const LoadingFallback = () => <div>🌟 Loading amazing content...</div>;

export const App = () => (
  <Suspense fallback={<LoadingFallback />}>
    <UILazyInView
      importer={() => import("./AmazingComponent")}
      exportName="default"
      componentProps={{}}
    />
  </Suspense>
);

Debugging & Troubleshooting

Common Issues

Component not loading:

// Check if component is within viewport
<UILazyInView
  importer={() => import("./Component")}
  exportName="default"
  componentProps={{}}
  onInView={() => console.log("Component is visible!")}
  rootMargin="0px" // Test with no margin
/>

Wrong export name:

// ❌ Wrong - component exported as named export
export const MyChart = () => <div />;
<UILazyInView
  importer={() => import("./MyChart")}
  exportName="default"  // Should be "MyChart"
/>

// ✅ Correct
<UILazyInView
  importer={() => import("./MyChart")}
  exportName="MyChart"  // Matches the export name
/>

Bundle size not reduced:

// Ensure dynamic imports are not tree-shaken
// ❌ Wrong - webpack might bundle this
import Component from "./HeavyComponent";

// ✅ Correct - dynamic import
const importer = () => import("./HeavyComponent");

Performance issues:

// Profile with React DevTools
<UILazyInView
  importer={() => {
    console.time("import");
    return import("./Component").finally(() => console.timeEnd("import"));
  }}
  exportName="default"
  componentProps={{}}
/>

📚 API Reference

UILazyInView Props

| Prop | Type | Default | Description | | ----------------- | --------------------------------------------- | ----------------------- | ---------------------------------------------------------------------------------------------------------------- | | importer | () => Promise<TModule> | - | Function that returns a dynamic import promise | | select | (module: TModule) => React.ComponentType<T> | - | Type-safe selector for the component export | | exportName | string | - | Legacy alternative to select; selects an export by its runtime string name | | componentProps | T | - | Props to pass to the lazy-loaded component | | threshold | number | 0.5 | IntersectionObserver threshold (0-1) | | placeholder | React.ReactNode | null | Component to show while loading | | loadOnMount | boolean | false | Force load immediately on mount, regardless of viewport visibility | | loadOnCondition | boolean | false | Load when this boolean becomes true (enables sequential loading, click/hover triggers, dependency-based loading) | | forwardRef | React.Ref<HTMLDivElement> | - | Ref to forward to the wrapper div | | onInView | () => void | - | Callback when component enters viewport | | rootMargin | string | '1000px 0px' | IntersectionObserver root margin | | loadState | LazyLoadState | LazyLoadState.DEFAULT | Loading strategy (used internally with loadOnMount/loadOnCondition) |

One component selector is required: use the recommended select prop or the backward-compatible exportName prop.

Loading Behavior

The component uses a priority system for determining when to load:

  1. loadOnMount={true}: Forces loading immediately when the component mounts (highest priority, ignores viewport)
  2. loadOnCondition={true}: Loads when the condition becomes true (medium priority)
  3. Default (viewport-based): Loads immediately if component is visible on mount, otherwise loads when it enters viewport

LazyLoadState

The loadState prop controls the loading strategy and is automatically set based on the boolean flags:

  • DEFAULT: Load immediately if visible on mount, otherwise when component enters viewport
  • LOADED_ON_MOUNT: Force load immediately on mount (when loadOnMount={true})
  • LAZY_ON_CONDITION: Load based on custom condition (when loadOnCondition={true})

🔧 Peer Dependencies

This package requires the following peer dependencies:

  • react >= 17.0.0
  • react-dom >= 17.0.0

📄 License

MIT