Most components you write return the same thing every time. Props go in, the same shape of markup comes out, nothing to think about. A dynamic component is the other kind. What it gives back depends on what it receives at runtime, so one component can render a text input on this pass and a date picker on the next.
Dashboards do this. Multi-step forms do it constantly. So does any admin panel where the signed-in user’s permissions decide half of what appears on screen. It’s a small rendering decision that shows up in an enormous amount of code: npm registry data from npmtrends.com puts the react package at roughly 173 million weekly downloads as of 2026.
What Is a Dynamic Component in React?

Nothing in the library marks a component as dynamic. React.js ships no separate type for it, no wrapper, no flag you set. Any function component qualifies the moment its markup depends on something that can change while the app is running.
A static component ignores all of that and returns identical JSX no matter what you hand it.
What moves the output is usually props coming down from a parent, or state a hook is holding onto. It can also be data from outside the component entirely: an API response, a config object, a schema someone edits in a CMS. Any of those shift, the component re-renders, and it can come back with different JSX than it had a second ago.
A dashboard widget that swaps its chart type off a dropdown value counts. So does a form field that turns from a text input into a date picker because the schema said so. Or a list item picking its icon component from the data type it was handed. None of that needs a special React feature. It just needs output that isn’t fixed at write time.
How Does Conditional Rendering Create Dynamic Output?
Pick which JSX to return based on a prop, a piece of state, or a plain boolean check. React re-runs that logic on every single render, so the same component can hand back one branch now and a different one two seconds later. That’s the whole mechanism.
Ternary and Logical AND Patterns
A ternary picks between exactly two outputs off one condition. Logical AND is the cheaper version: render this, or render nothing at all. Loading state versus loaded state, signed in versus signed out, a ternary covers both without ceremony.
Nesting them reads badly past two levels. I’ve opened files with three-deep ternaries in production and nobody, including the person who wrote them, could say what rendered when. A switch statement or a lookup table is the better call well before that point.
Switch Statements for Multiple Outcomes
Once you have three or more distinct outcomes, a switch stays readable where stacked ternaries stop being readable.
Status fields are the usual suspect: pending, approved, rejected. Field types too, text and select and date and checkbox. User roles, same idea.
Skip the default case and you get a component that silently renders nothing the day someone adds a value nobody planned for. Put a default in even if all it does is log something.
Which Hooks Control a Dynamic Component’s State?
useState holds one changing value. useReducer takes over when the state has several fields that move together. useEffect isn’t really in the same category, since it doesn’t decide what renders at all, it just reacts after a render when a prop or state value changed.
All of them follow the rules of hooks: top level only, never inside a condition or a loop, same order on every render. Break that and the errors you get back are rarely helpful.
A closer walkthrough of how each hook behaves lives in the guide to React hooks.
useState for Simple Values
One piece of state, a getter and a setter, one call. That’s it.
| Hook | Stores | Typical Use |
|---|---|---|
| useState | A single value or object | Toggle a panel, hold a form field |
| useReducer | Multiple related fields with defined actions | Multi-step forms, complex filters |
| useEffect | Side effects, not state itself | Fetching data after props change |
Calling the setter with a new value schedules a re-render, and that re-render is what actually changes the output on the next pass.
useReducer for Complex State
The switch happens once a component tracks more than two or three related fields that update together. Multi-step forms hit this fast. Filter panels with a handful of toggles get there too.
Updates go through one reducer function instead of five scattered setter calls, which is the main thing you’re buying. Actions have names, so a bug report can point at the exact action that fired instead of “the form broke”. Keeping related fields in a single object also cuts down on one update quietly overwriting another.
Four useState calls tracking related data is usually the signal. Not a rule. Just a smell.
useEffect for Reacting to Changes
useEffect runs after the render, reacting to a change in a prop or a state value rather than deciding what shows up in the first place.
The common job in a dynamic component is fetching new data when a prop switches. A user ID changes, the component needs a different profile, the effect handles the request.
Where it goes wrong is almost always the dependency array:
- Leaving out a prop or state value the effect actually reads
- Passing a function or object that gets recreated on every render
- Using an empty array on an effect that depends on props
Dan Abramov, who spent years on the React team, has written at length about how a single missing dependency leaves an effect reacting to stale props instead of current ones.
How Does React Decide When to Re-Render a Dynamic Component?
State changes. A parent re-renders and passes new props. A context value the component reads changes. Any of those schedules a re-render, and from there React builds a new virtual DOM tree and compares it against the previous one instead of touching the real DOM directly.
React’s documentation has long explained that a general-purpose tree diff on a list of 1,000 elements would need on the order of a billion comparisons, far too slow to run on every update. So the library cuts a corner deliberately, with a heuristic algorithm that finishes in linear time by assuming elements of different types always produce different subtrees.
The Key Prop Rule
Every item rendered from an array needs a stable, unique key. That’s how React matches old elements to new ones by identity rather than by position.
Without one, reordering a list can attach the wrong internal state to the wrong item. Array indexes work fine, technically, right up until the list reorders or filters or loses something from the middle. Then the symptom gets ugly: a checkbox, an expanded panel, or a half-typed input stuck on the wrong row after a sort.
A component that maps over dynamic data and skips the key prop entirely falls back to the index, which is the exact case most likely to trigger this.
Fiber and Diffing in Practice
React Fiber is the engine underneath the scheduling. It breaks rendering work into units it can pause, prioritize, and resume, instead of blocking the browser for one long synchronous pass.
That matters most for a component re-rendering constantly. A live search box. A chart redrawing on every scroll event.
For techniques that go further than the default diffing behavior, the guide on React performance optimization covers batching and profiling in more detail.
Worth being clear about what Fiber doesn’t do. It doesn’t change what gets rendered, only how the work gets scheduled, which is why a dynamic component doing heavy computation still needs memoization on top of Fiber rather than instead of it.
What Composition Patterns Enable Dynamic Component Behavior?
Sometimes the branching doesn’t belong inside one component’s JSX. Component mapping, factory functions, render props, and higher-order components all move the decision up a level, swapping in a different component or a different piece of logic based on runtime input.
They sit inside the broader set of React component patterns developers reach for once a component needs to behave differently depending on the data it gets.
Component Mapping and Factory Functions
Component mapping is a plain object where each key points at a component. Rendering turns into a lookup instead of a chain of conditions, which is about as boring and reliable as this gets.
| Pattern | Mechanism | Best Fit |
|---|---|---|
| Component map | Object lookup by type key | A fixed, known set of types |
| Factory function | Function returns a component based on an argument | Types that need extra setup logic |
A factory function goes a step further. Instead of a static lookup, a function decides which component to return, and it can attach extra props or wrapping logic on the way out.
Render Props and Children as a Function
A render prop is a function you pass in that the component calls to decide what to render. Control of the output goes back to whoever is using the component.
Children as a function is the same trick aimed at the children prop, called rather than rendered directly.
It earns its keep when you want to share logic like mouse position or fetch state without dictating a markup shape, or when the parent should own the visuals while the child owns the behavior.
Downshift, a well known library for building accessible dropdowns and comboboxes, popularized this for exactly that reason. The keyboard navigation logic stays fixed. The markup stays wide open.
Higher-Order Components
An HOC takes a component as an argument and returns a new one wrapped with extra props, extra state, or extra behavior.
Before hooks existed this was how you shared logic across components, and plenty of production codebases still carry HOCs from that era.
Two places it still holds up: wrapping a component with authentication checks before it ever mounts, and injecting the same set of props into several unrelated components.
Custom hooks have taken over most new uses. But nobody rips out working HOCs just because a newer option exists, and they shouldn’t.
How Do You Create a Dynamic Component Step by Step?
The order below holds whether the thing renders form fields, dashboard widgets, or list items.
- Define the component map or the list of type values first, before writing any rendering logic
- Build the base component that receives the data deciding which output to show
- Wire state or a prop to the field that controls which branch renders
- Render through the map or the switch instead of hardcoding each case inline
- Test with at least two different runtime inputs to confirm both branches actually mount correctly
Skipping the first step is where this usually falls apart. Someone starts writing conditions before deciding what the set of types actually is, and the component grows one ternary at a time until nobody wants to open the file.
Keep a React cheat sheet nearby while wiring this up. Hook syntax and JSX rules are easy to mix up when a component is doing several of these things at once.
Example: A Dynamic Field Renderer
A form rendering different input types from a schema is about the clearest version of prop-driven behavior there is.
| Field Type | Rendered As | Data Source |
|---|---|---|
| text | Standard text input | schema field type |
| select | Dropdown with an options array | schema field options |
| date | Date picker component | schema field type |
| checkbox | Boolean toggle | schema field type |
Four entries in the map, one per field type. The renderer never needs to know how many fields exist. It walks the schema array and looks up each type.
Adding a fifth field type later means adding one line to the map. The rendering logic doesn’t get touched.
How Does Code Splitting Work for a Dynamic Component?
One bundle becomes several smaller chunks, and a chunk downloads only when a component actually needs to render. React.lazy wraps a dynamic import call. Suspense shows a fallback while the network does its work.
The numbers are worth knowing before deciding it’s worth the trouble. One documented case study reported a production React bundle going from 1,542 KB to 971 KB, a 37 percent reduction, purely from moving routes over to React.lazy with React Router, before any further optimization work. A 2022 study published in the International Journal of Core Engineering & Management found that combining lazy loading with code splitting can cut page load time by as much as 40 percent.
The flip side gets mentioned far less often. For a very small component, the network round trip needed to fetch it, commonly cited in the 100 to 300 millisecond range on a 3G connection, costs more than the few kilobytes you saved by leaving it out of the main bundle. Lazy loading pays off on larger or rarely opened screens, not on a 2 KB badge component.
React.lazy and Suspense
React.lazy takes a function that calls import() and returns that as a component, holding off on the network request until the component actually mounts. Suspense has to sit somewhere above it in the tree, and one boundary can cover several lazy components at once.
What you get is a smaller initial bundle, which moves first paint and time to interactive in the right direction, plus screens like an admin panel or a settings page that load only when somebody opens them.
It isn’t free, though. Every boundary needs a Suspense fallback or the screen just freezes with no feedback at all. And a failed import throws an error a normal try or catch block will not catch, so that case needs an error boundary.
Route-based splitting through React Router is where most teams start, since a route is already a natural place to draw a loading boundary.
Dynamic import Syntax
The import() function belongs to the ECMAScript standard, not to React, and it returns a promise that resolves to the requested module.
Bundlers read the syntax and generate a separate chunk on their own. Webpack and Vite both handle the basic case with no configuration.
What each one adds on top differs a bit. Webpack supports magic comments like webpackPrefetch and webpackPreload for hinting at when a chunk should load. Vite handles the same syntax natively through its Rollup-based build step. Babel transforms import() for older browser targets that don’t support it.
None of that changes how the component code reads. The dynamic import call looks identical whether Webpack or Vite ends up bundling it.
Which Method Should You Use to Render a Dynamic Component?
It depends on how many outcomes exist and whether the component needs to skip loading something upfront. Conditional rendering, component mapping, lazy loading, and render props each solve a different shape of the same problem.
| Method | Best For | Weak Point |
|---|---|---|
| Conditional rendering | Two or three fixed outcomes | Unreadable past three branches |
| Component mapping | An open-ended or growing set of types | Needs a map to maintain |
| Lazy loading | Components not needed on first render | Adds a Suspense boundary to manage |
| Render props | Sharing logic across unrelated components | Nesting gets hard to trace past two levels |
Two or three known outcomes, reach for a ternary or a switch. A list of types that keeps growing, build the map. Something most visitors never open, React.lazy and Suspense. Logic several unrelated components need, a render prop.
Most production components end up combining two of these anyway, rather than picking one and living with it forever.
When Should You Optimize a Dynamic Component with Memo Hooks?
After profiling shows a specific re-render or a specific calculation is slow. Not before. React.memo, useMemo, and useCallback all cost something, and adding them without evidence usually buys comparison overhead without removing any real work.
Memoization pays off when the wrapped calculation or function is genuinely expensive. Trivial computations rarely benefit, and the equality check React runs on every render can easily outweigh whatever you saved.
React.memo for Skipping Re-Renders
React.memo wraps a component and skips its re-render when the props haven’t changed since last time.
It earns its cost when a component sits deep in a tree that re-renders often for reasons unrelated to it, and when that component is expensive to render rather than merely convenient to skip.
Wrap something that renders in under a millisecond and you’ve added a prop comparison on every render for no gain at all.
useMemo and useCallback for Referential Stability
useMemo caches the result of a calculation. useCallback caches a function’s identity, so a child wrapped in React.memo doesn’t see a brand new prop every render.
Both hold onto memory and run an equality check on every render. Neither is free.
| Hook | Caches | Skip When |
|---|---|---|
| useMemo | A computed value | The computation is cheap |
| useCallback | A function reference | The function is not passed to a memoized child |
A stale function reference sitting inside a dependency array is a different problem entirely, and it comes up again further down as one of the more common ways this whole setup breaks.
When Does a Dynamic Component Not Apply?
When the output never actually changes. When the abstraction costs more than it saves. When the extra logic lands somewhere performance-sensitive and buys nothing visible.
The static version wins for a UI element that never changes shape, like a footer or a fixed set of navigation links or a disclaimer block. It also wins when you’re mapping over two or three types and a plain conditional is one line shorter and easier to scan. And state-driven rendering dropped into a path that already runs on every keystroke or scroll event is asking for trouble.
Lazy loading fails the same test when the component is small. The Suspense boundary, the fallback UI, and the network round trip together cost more than just leaving the thing in the main bundle would have.
None of these are edge cases. They’re the default result of applying a dynamic pattern to a problem that never called for one.
Which Tools Support Building and Testing a Dynamic Component?
TypeScript, PropTypes, ESLint, Jest, Storybook, and React Developer Tools each catch a different class of problem, from wrong prop shapes to broken hook rules.
eslint-plugin-react-hooks on its own sees roughly 28.8 million weekly downloads on the npm registry, which tells you how many teams treat hook linting as non-negotiable.
Typing Props with TypeScript

Type a dynamic component’s props as a union and the compiler flags a missing case the moment someone adds a new type to the map.
A union catches a prop that doesn’t exist on a given type, a new variant nobody handled in the component map, and the classic mix-up over which fields are required for which type.
PropTypes gives you a lighter version of the same idea at runtime rather than compile time, which is still useful on a codebase that hasn’t adopted TypeScript.
A closer look at typing hooks, props, and generics together lives in the guide to React with TypeScript.
TypeScript’s grip on the React ecosystem was already settled by the State of JavaScript 2023 survey, where nearly a third of respondents reported writing TypeScript exclusively and almost three-quarters said they used it more than half the time.
Testing with Jest and Storybook

Jest runs the assertions confirming that a dynamic component renders the right branch for a given prop or state value. It was reported by 7,262 respondents in the State of JavaScript 2024 survey, more than any other testing tool in that survey.
The split between the two tools is clean enough. Jest confirms the logic, meaning the right branch renders for a given input. Storybook confirms the appearance, meaning how each branch actually looks and behaves in isolation.
Storybook’s own documentation lists GitHub, Airbnb, and Stripe among the teams using it to build interfaces.
A broader comparison of testing options, including where Cypress and React Testing Library fit alongside Jest, lives in the guide to React testing libraries.
React Developer Tools rounds it out. Being able to inspect exactly which props and state a component received on a given render matters most when it stubbornly refuses to render the branch you expected.
What Common Mistakes Break a Dynamic Component?
Most bugs here trace back to unstable keys, inline props quietly defeating memoization, or a dependency array that lies about what a hook actually reads.
Missing or Unstable Keys
This one shows up as a performance problem more than a data problem.
An unstable key forces React to unmount and remount a row instead of updating it in place. CSS transitions restart mid-animation. Focus gets stolen from whatever input the user was typing into.
| Symptom | Cause |
|---|---|
| Lost focus mid-typing | Row remounted instead of updated |
| Animation restarts | New DOM node replaces the old one |
| Extra DOM operations | React cannot match old and new elements |
Switch the key to a stable field, a database ID being the obvious one, and both the correctness issue covered earlier and the remount cost go away in the same change.
Inline Functions Defeating Memoization
An inline arrow function or a fresh object literal passed as a prop creates a new reference on every render, even when nothing inside it changed.
React.memo compares props by reference by default, so a child wrapped in it re-renders anyway the second it receives one of these.
The two usual culprits are an inline onClick handler written straight into the JSX instead of stored in useCallback, and a style or config object literal created fresh inside the render function.
Fix it by wrapping the function in useCallback or the object in useMemo one level up, so the reference survives renders that didn’t actually change anything.
Worth remembering that a component can carry React.memo, useCallback, and useMemo all at once and still re-render every single time if one prop keeps showing up as a brand new object.
Wrong Hook Dependencies
A stale closure is the sharper version of this. A function created inside a hook keeps pointing at the value a variable held when the function was created, not the current one.
Where it tends to appear:
- An interval or timeout set up inside useEffect that reads state from outside its own dependency array
- A useCallback with an incomplete dependency list, passed down and called later with outdated data
- An event listener attached once that never picks up a later state update
Kent C. Dodds has written extensively about this exact pattern. The fix is almost always the same: add the missing value to the dependency array, or switch to the updater-function form of the state setter so the callback stops needing the value at all.
FAQ on How To Create Dynamic Component In React.Js
Can You Build a Dynamic Component Without JSX?
Yes. JSX is syntactic sugar over React.createElement, and calling createElement directly with a variable type argument works exactly the same.
Readability suffers, obviously. But the pattern itself, picking a branch at runtime, never depended on JSX in the first place.
Do Dynamic Components Behave the Same in Class Components as Functional Components?
Class components handle dynamic output through the same conditional logic inside render(), with lifecycle methods like componentDidUpdate reacting to prop changes.
Functional components use hooks instead. The rendering decision itself, ternary or switch or component map, works identically in both.
Is a Dynamic Component the Same Thing as a Controlled Component?
No. A controlled component has its value driven entirely by React state, which is mostly a forms concept.
Dynamic describes changing output, not input control. Different problems. A single component can easily be both at the same time.
Does Dynamic Rendering Hurt SEO in a Server-Rendered React App?
Not by default. Frameworks like Next.js resolve the branch on the server before sending HTML, so crawlers see the same finished markup a user does.
Trouble starts only when a branch depends on client-only data such as localStorage.
What Are Dynamic Components Used for in Real Apps?
Role-based dashboards that swap widgets by permission. Product configurators changing input types based on a selected variant.
Notification centers rendering a different card layout per alert type. CMS-driven pages where the schema, not the developer, decides what appears.
What Should You Fix First in How To Create Dynamic Component In React.Js?
Start with the key prop, not with hooks or memoization. An unstable key breaks list correctness long before rendering speed becomes the problem actually worth chasing.
Correctness comes before speed in every repair order:
- Stable keys on every mapped list
- Correct dependency arrays on useEffect and useCallback
- Memoization only where profiling shows a real cost
Fixing keys first means an extra re-render pass ships before memoization lands. That’s a small performance cost traded for a list that behaves correctly right away, which is the right trade almost every time.
React’s weekly downloads next to the hook linter’s weekly downloads give a ratio worth sitting with: roughly one React install in six also runs eslint-plugin-react-hooks.
The logical next step covers displaying updating data to users in React.js, since a dynamic component’s branch only matters once the data behind it changes after that first render.
- C# cheat sheet - September 11, 2026
- Website Improvements That Can Support Business Growth - September 11, 2026
- What Are Android Vitals? A Simple Guide for Developers - September 10, 2026



