Overview
Whilepreact/compat provides excellent React compatibility, there are some differences in behavior and implementation details. Most applications won’t encounter these, but it’s important to be aware of them.
In most cases, preact/compat provides 100% compatibility with React for standard usage patterns. Differences typically only matter for edge cases or advanced usage.
Core Differences
Synthetic Events
Preact uses native browser events instead of React’s synthetic event system. React:The compat layer adds a no-op
persist() method to events at compat/src/render.js:100 for compatibility, but it’s not needed since Preact uses native events.- Native events are not pooled (no need for
e.persist()) - Event properties can be accessed asynchronously without issues
- Slightly better performance due to no synthetic event overhead
Event Naming
Preact automatically normalizes React event names to their browser equivalents:className vs class
Preact supports bothclass and className props:
Lifecycle Methods
Unsafe Lifecycle Methods
Preact supports both prefixed and unprefixed unsafe lifecycle methods through property getters/setters:While these lifecycle methods are supported, they’re deprecated in React and should be avoided in new code.
Concurrent Features
Transitions and Deferred Values
useTransition and useDeferredValue are implemented but don’t provide true concurrent rendering:
startTransitionis a passthrough function at compat/src/hooks.js:52useDeferredValuereturns the value unchanged at compat/src/hooks.js:56useTransitionalways returns[false, startTransition]at compat/src/hooks.js:60
flushSync
flushSync is implemented as a passthrough in Preact:
Suspense and Lazy Loading
Suspense and lazy loading are fully supported:- Error boundaries for promise rejection
- Proper fallback rendering
- Support for nested Suspense boundaries
Rendering Differences
Render Return Value
Preact’s render returns the component instance, matching React’s behavior:Container Clearing
React clears container content on first render:Hydration
Hydration works similarly to React:Props and Attributes
defaultValue and value
The compat layer handlesdefaultValue as a fallback for value:
SVG Attributes
Preact automatically converts camelCase SVG attributes to kebab-case:Style Object
Numeric style values automatically get ‘px’ appended (except for unitless properties):Boolean Attributes
The download attribute withtrue value becomes an empty string:
Component Features
PureComponent
PureComponent performs shallow comparison of props and state:shallowDiffers utility from compat/src/util.js:9.
memo()
Thememo higher-order component works identically to React:
forwardRef()
forwardRef is fully supported:
- Adding the
$$typeofsymbol - Exposing a
renderproperty - Setting
isReactComponentflag
Children API
The Children API is fully compatible:StrictMode
StrictMode is a no-op in Preact (it’s aliased to Fragment):Utilities
isValidElement
Checks if a value is a valid Preact/React element:$$typeof property at compat/src/index.js:58.
findDOMNode
Legacy API that’s supported but deprecated:unmountComponentAtNode
Hooks Differences
useInsertionEffect
useInsertionEffect is aliased to useLayoutEffect in Preact:
useSyncExternalStore
useSyncExternalStore is fully implemented:
Server-Side Rendering
renderToString
Server rendering is fully supported:Known Limitations
1. Concurrent Rendering
Preact doesn’t implement React’s concurrent rendering features:- Time slicing
- Selective hydration
- Priority-based rendering
2. Server Components
React Server Components (RSC) are not supported:3. Error Boundaries in Render
Some edge cases with error boundaries may differ from React.4. Deep React Internals
Libraries that access React internals via__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED may not work:
5. Legacy Context API
The legacy context API (getChildContext) has limited support compared to the modern Context API.Performance Differences
Bundle Size
Preact with compat is significantly smaller than React:- Preact core: ~3kb gzipped
- Preact + compat: ~5-6kb gzipped
- React + ReactDOM: ~40kb+ gzipped
Runtime Performance
Preact is generally faster than React due to:- No synthetic event system overhead
- Simpler reconciliation algorithm
- Smaller runtime means better parsing time
- Direct DOM manipulation
Compatibility Overhead
The compat layer adds:- ~2-3kb to bundle size
- Minimal runtime overhead for prop normalization
- Event handling normalization
If you don’t need React compatibility, importing from
preact and preact/hooks directly instead of using the compat layer will give you the smallest possible bundle.Best Practices
-
Use the compat layer for third-party libraries only: Import from
preactdirectly in your own code when possible. - Test thoroughly: While most code works identically, test your app’s critical paths.
- Check library compatibility: Before adopting a React library, verify it works with Preact (most do).
- Avoid React internals: Don’t rely on undocumented React internals.
- Use refs over findDOMNode: Modern ref patterns are better supported.
- Monitor bundle size: Use tools like webpack-bundle-analyzer to track your bundle.
When to Use Preact
Preact with compat is ideal when:- Bundle size is critical (mobile, slow networks)
- You want React’s API with better performance
- You’re building a new project and want React library compatibility
- You’re migrating from React and want minimal code changes
- You need concurrent rendering features
- You’re using React Server Components
- You have deep integrations with React internals
- Your team is heavily invested in React-specific tooling
Next Steps
Compat Overview
Review all supported React features
Migration Guide
Migrate your React app to Preact