Differences to React
Preact itself is not intended to be a reimplementation of React. There are differences. Many of these differences are trivial, or can be completely removed by using preact/compat, which is a thin layer over Preact that attempts to achieve 100% compatibility with React.
The reason Preact does not attempt to include every single feature of React is in order to remain small and focused - otherwise it would make more sense to simply submit optimizations to the React project, which is already a very complex and well-architected codebase.
Main differences
The main difference when comparing Preact and React apps is that we don't ship our own Synthetic Event system. Preact uses the browser's native addEventListener
for event handling internally. See GlobalEventHandlers for a full list of DOM event handlers.
For us it doesn't make sense as the browser's event system supports all features we need. A full custom event implementation would mean more maintenance overhead and a larger API surface area for us.
We've come across the following differences between React's synthetic event system and native browser events:
- Events don't bubble through
<Portal>
components - The clear "x" button for
<input type="search">
does not fire aninput
event in IE11 - useonSearch
instead - Use
onInput
instead ofonChange
for<input>
elements (only ifpreact/compat
is not used) - Use
onDblClick
instead ofonDoubleClick
(only ifpreact/compat
is not used)
The other main difference is that Preact follows the DOM specification more closely. An example of this is the ability to use class
instead of className
.
Version Compatibility
For both preact and preact/compat, version compatibility is measured against the current and previous major releases of React. When new features are announced by the React team, they may be added to Preact's core if it makes sense given the Project Goals. This is a fairly democratic process, constantly evolving through discussion and decisions made in the open, using issues and pull requests.
Thus, the website and documentation reflect React
16.x
and15.x
when discussing compatibility or making comparisons.
Debug messages and errors
Our flexible architecture allows addons to enhance the Preact experience in any way they want. One of those addons is preact/debug
which adds helpful warnings and errors and attaches the Preact Developer Tools browser extension, if installed. Those guide you when developing Preact applications and make it a lot easier to inspect what's going on. You can enable them by adding the relevant import statement:
import "preact/debug"; // <-- Add this line at the top of your main entry file
This is different from React which requires a bundler being present that strips out debugging messages at build time by checking for NODE_ENV != "production"
.
Features unique to Preact
Preact actually adds a few convenient features inspired by work in the (P)React community:
Native support for ES Modules
Preact was built with ES Modules in mind from the get go and therefore was one of the first frameworks to support them. You can load Preact via the import
keyword directly in browsers without having it to pass through a bundler first.
Arguments in Component.render()
For convenience we pass this.props
and this.state
of a class component to the render()
. Take a look at this component which uses one prop and one state property.
// Works in both Preact and React
class Foo extends Component {
state = { age: 1 };
render() {
return <div>Name: {this.props.name}, Age: {this.state.age}</div>;
}
}
Run in REPLIn Preact this can be also written like this:
// Only works in Preact
class Foo extends Component {
state = { age: 1 };
render({ name }, { age }) {
return <div>Name: {name}, Age: {age}</div>;
}
}
Run in REPLBoth snippets render the exact same thing. It's just a matter of stylistic preference.
Raw HTML attribute/property names
With Preact we follow more closely the DOM specification supported by all major browsers. One prominent difference is that you can use the standard class
attribute instead of className
.
// This:
<div class="foo" />
// ...is the same as:
<div className="foo" />
Run in REPLMost Preact developers prefer to use class
because it's shorter to write, but both are supported.
SVG inside JSX
SVG is pretty interesting when it comes to the names of its properties and attributes. Some properties (and their attributes) on SVG objects are camelCased (e.g. clipPathUnits on a clipPath element), some attributes are kebab-case (e.g. clip-path on many SVG elements), and other attributes (usually ones inherited from the DOM, e.g. oninput
) are all lowercase.
Preact forwards SVG-Attributes as is. This allows you to copy and paste unmodified SVG snippets right into your code and have them work out of the box. This allows greater interoperability with tools designers tend to use to generate icons or SVG illustrations.
If you're coming from React you're likely used to specify every attribute in camelCase. If you'd like to continue using the camelCase'd attribute names you can use our preact/compat compatibility layer. It mirrors the React API and normalizes these attributes.
// React
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<circle fill="none" strokeWidth="2" strokeLinejoin="round" cx="24" cy="24" r="20" />
</svg>
// Preact (note stroke-width and stroke-linejoin)
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
<circle fill="none" stroke-width="2" stroke-linejoin="round" cx="24" cy="24" r="20" />
</svg>
Run in REPLUse onInput
instead of onChange
Largely for historical reasons, the semantics of React's onChange
event are actually the same as the onInput
event provided by browsers, which is supported everywhere. The input
event is the best-suited event for the majority of cases where you want to react when a form control is modified. In Preact core, onChange
is the standard DOM change event that gets fired when an element's value is committed by the user.
// React
<input onChange={e => console.log(e.target.value)} />
// Preact
<input onInput={e => console.log(e.target.value)} />
Run in REPLIf you're using preact/compat, most onChange
events are internally converted to onInput
to emulate React's behavior. This is one of the tricks we use to ensure maximum compatibility with the React ecosystem.
JSX Constructor
JSX is a syntax extension for JavaScript that is converted to nested function calls. The idea of using these nested calls to build up tree structures long predates JSX, and was previously popularized in JavaScript by the hyperscript project. This approach has value well beyond the scope of the React ecosystem, so Preact promotes the original generalized community-standard. For a more in-depth discussion of how JSX works and its relationship to Hyperscript, read this article.
Source: (JSX)
<a href="/">
<span>Home</span>
</a>
Run in REPLOutput:
// Preact:
h(
'a',
{ href:'/' },
h('span', null, 'Home')
);
// React:
React.createElement(
'a',
{ href:'/' },
React.createElement('span', null, 'Home')
);
Run in REPLUltimately, if you're looking at the generated output code for a Preact application, it's clear that a shorter un-namespaced "JSX pragma" is both easier to read and more suitable for optimizations like minification. In most Preact apps you'll encounter h()
, though it doesn't really matter which name you use since a createElement
alias export is also provided.
No contextTypes needed
The legacy Context
API requires Components to declare specific properties using React's contextTypes
or childContextTypes
in order to receive those values. Preact does not have this requirement: all Components receive all context
properties produced by getChildContext()
by default.
Features exclusive to preact/compat
preact/compat
is our compatibility layer that translates React code to Preact. For existing React users this can be an easy way to try out Preact without changing any of your code, by setting up a few aliases in your bundler configuration.
Children API
The Children
API is a specialized set of methods for working with the value of props.children
. For Preact this is generally unnecessary, and we recommend using the built-in array methods instead. In Preact, props.children
is either a Virtual DOM node, an empty value like null
, or an Array of Virtual DOM nodes. The first two cases are the simplest and most common, since it's possible to use or return children
as-is:
// React:
function App(props) {
return <Modal content={Children.only(props.children)} />
}
// Preact: use props.children directly:
function App(props) {
return <Modal content={props.children} />
}
Run in REPLFor specialized cases where you need to iterate over the children passed to a component, Preact provides a toChildArray()
method that accepts any props.children
value and returns a flattened and normalized Array of Virtual DOM nodes.
// React
function App(props) {
const cols = Children.count(props.children);
return <div data-columns={cols}>{props.children}</div>
}
// Preact
function App(props) {
const cols = toChildArray(props.children).length;
return <div data-columns={cols}>{props.children}</div>
}
Run in REPLA React-compatible Children
API is available from preact/compat
to make integration with existing component libraries seamless.
Specialised Components
preact/compat ships with specialised components that are not necessary for every app. These include
- PureComponent: Only updates if
props
orstate
have changed - memo: Similar in spirit to
PureComponent
but allows to use a custom comparison function - forwardRef: Supply a
ref
to a specified child component. - Portals: Continues rendering the current tree into a different DOM container
- Suspense: experimental Allows to display fallback content in case the tree is not ready
- lazy: experimental Lazy load async code and mark a tree as ready/not ready accordingly.