
Import On Interaction
- 1 installs
- 73.4k repo stars
- Updated June 18, 2026
- thedaviddias/frontendchecklist
Defers non-critical modules like chat widgets and editors behind clicks, focus, or hover using dynamic import to shrink the initial bundle.
About
A frontend-checklist performance rule for loading non-critical code on user interaction. A developer uses it so users do not pay download and parse cost for features they may not use.
- Move non-essential code behind clicks, focus, or hover
- Use dynamic import() and show immediate UI feedback
Import On Interaction by the numbers
- 1 all-time installs (skills.sh)
- Ranked #1,912 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/thedaviddias/frontendchecklist --skill import-on-interactionAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1 |
|---|---|
| repo stars | ★ 73.4k |
| Last updated | June 18, 2026 |
| Repository | thedaviddias/frontendchecklist ↗ |
What it does
Defers non-critical modules like chat widgets and editors behind clicks, focus, or hover using dynamic import to shrink the initial bundle.
Files
Load non-critical code on user interaction
Users should not pay the download, parse, and execution cost for features they may never use. Import-on-interaction shrinks initial JavaScript, reduces main-thread work during page load, and keeps the first view focused on content that matters immediately.
Quick Reference
- Move non-essential code behind clicks, focus, hovers, or explicit user actions
- Use dynamic import() so the code is absent from the initial route bundle
- Show immediate UI feedback while the deferred chunk loads
- Use this for chat widgets, editors, maps, filters, and export flows that are not needed on first paint
Check
Inspect this route or component for heavy modules, widgets, or third-party scripts that load on first render but are only needed after a user clicks, focuses, hovers, or opens a panel.
Fix
Move the non-critical dependency behind a user-triggered dynamic import(), add immediate loading feedback, and ensure the initial render still works without the deferred feature.
Explain
Explain import-on-interaction, why it improves initial performance, and which features are safe to defer until the user shows intent.
Code Review
Inspect event handlers, modal triggers, drawers, editors, maps, export actions, and third-party widgets. Flag code that is included in the initial bundle even though it is only needed after a user interaction, and verify the fix keeps the initial path lighter without degrading the triggered flow.
---
For full implementation details, code examples, and framework-specific guidance, see references/rule.md.
Rule page: https://frontendchecklist.io/en/rules/performance/import-on-interaction
Load non-critical code on user interaction
Defer JavaScript modules, widgets, and third-party code until the user signals intent through a click, focus, hover, or similar interaction.
Priority: high · Difficulty: intermediate · Time: 20 min
--- Import-on-interaction delays code loading until the user does something that proves the feature is relevant. It is especially useful for features that are expensive but optional on the initial view.
Code Examples
#
Plain JavaScript Dynamic Import
// ❌ Bad: Heavy code is loaded for every visitor
document.querySelector('#configure').addEventListener('click', () => {
openProductConfigurator()
})
// ✅ Good: Load only when the user asks for it
document.querySelector('#configure').addEventListener('click', async () => {
showConfiguratorSkeleton()
const { openProductConfigurator } = await import('./product-configurator.js')
openProductConfigurator()
})Load a Third-Party Widget After Intent
const chatButton = document.querySelector('#open-chat')
chatButton.addEventListener('click', async () => {
chatButton.disabled = true
chatButton.textContent = 'Loading chat...'
const { bootChat } = await import('./chat-loader.js')
await bootChat()
})React Example
const [loading, setLoading] = useState(false)
async function handleExport() {
setLoading(true)
const { exportReport } = await import('./export-report')
await exportReport()
setLoading(false)
}
return (
<button onClick={handleExport} disabled={loading}>
{loading ? 'Preparing export...' : 'Export report'}
</button>
)
}Why It Matters
- Smaller initial bundles: Users do not download feature code they may never use.
- Less main-thread work: Deferring parse and execution work reduces competition with critical rendering and input handling.
- Better prioritisation: The browser can focus on content, LCP resources, and essential app logic before optional features.
- Cleaner third-party strategy: Widgets like chat, reviews, maps, and rich editors often belong behind explicit intent instead of first paint.
When to Use It
Use import-on-interaction for:
- Chat widgets
- Rich text editors
- Map embeds
- Export and print flows
- Advanced filters, configurators, and comparison tools
Avoid it for:
- Navigation needed on the first screen
- The primary purchase or sign-in action
- Above-the-fold media or content needed for LCP
- Code required before the first user interaction can succeed
Verification
Use Chrome DevTools Coverage or a waterfall trace to confirm the deferred module is actually absent from the initial path, not just moved to a different eager bundle.
1. Confirm the deferred module is absent from the initial route bundle or first-load network waterfall. 2. Trigger the interaction and verify the import request starts immediately and the UI shows a loading state within roughly 100ms. 3. Re-test the same flow and confirm repeat interactions reuse the cached chunk without another full download. 4. Measure the route before and after the change and confirm the initial JavaScript cost or main-thread work decreases. 5. Verify the interaction still feels responsive on a throttled mobile profile and does not create new long tasks above 50ms before the deferred feature starts loading.