The desktop, tablet and mobile buttons at the top of the site editor are gone. In their place is a preview you can drag to any width you like. That sounds like a small interface tweak. It is actually a change in how WordPress thinks about responsive design, and it takes one hook with it.
useResizeCanvas() is deprecated and non-operational as of Gutenberg 23.5. If your block or plugin called it to find out which device preview was active, it now returns nothing useful. There is no drop-in replacement, because the concept it described no longer exists.
Here is what changed in the editor, what it means for code that assumed three fixed viewports, how the new @ prefix syntax for responsive states works, and what else moved in the same release.
What the editor does now
The old model gave you three states. You clicked desktop, tablet or mobile, and the editor canvas jumped to a fixed width representing that device. Behind that was a hard assumption: that there are three meaningful widths and every layout decision can be expressed against them.
Gutenberg 23.5 replaces the toggle with a unified, freely resizable device preview. You drag the canvas edge and it resizes continuously. There is no snapping to three blessed widths, because there are no blessed widths.
This lines up with where CSS has been heading for several years. Fixed breakpoints were always a proxy for the real question, which is whether the content has enough room. Container queries answered that properly at the CSS level, and the editor is catching up to the same idea: you test at the width where your layout breaks, not at the width a phone from 2015 happened to be.
An official call for testing went out on 3 July 2026, which is the signal that the design is settled enough to build against.
The hook that stopped working
useResizeCanvas() existed to answer one question: what width should the canvas be, given the currently selected device? It took a device type and returned the styles to apply.
// Old pattern. The hook is now deprecated and non-operational.
import { useResizeCanvas } from '@wordpress/block-editor';
function MyEditorCanvas( { deviceType } ) {
const resizedCanvasStyles = useResizeCanvas( deviceType );
return <div style={ resizedCanvasStyles }>{ /* ... */ }</div>;
}With a continuously resizable canvas, the question the hook answered is meaningless. There is no width implied by a device name, because the user picks the width directly.
The important detail is that this is a non-operational deprecation rather than a removal. Calling it will not throw. It returns without doing the thing you expected, which means any layout depending on its return value silently gets nothing. That is the same failure shape as the icon and component changes in this release: no error, wrong result.
What still works
getDeviceType() and setDeviceType() remain functional. If you have code that reads or sets the current preview mode, it keeps working:
import { useSelect, useDispatch } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
const deviceType = useSelect(
( select ) => select( editorStore ).getDeviceType(),
[]
);
const { setDeviceType } = useDispatch( editorStore );Be careful about what you conclude from that value now. It tells you which named preview the user last selected. It does not tell you the canvas width, because the user can drag to any width from any starting point. Code that maps a device name to a pixel width is making an assumption the editor no longer guarantees.
Migrating code that used the hook
Three patterns cover most real usage.
If you were sizing your own canvas
Stop. The editor owns canvas sizing now, and a plugin that also sizes the canvas will fight the resize handle. Remove the code and let the editor do it.
This is the most common case and it is a deletion rather than a migration. Most plugins that called the hook were reimplementing something core already did, usually because the plugin predates core doing it well.
If you were changing rendering based on device
This is the case that needs real work, because you were asking the wrong question and getting away with it.
The right question was never “is this mobile” but “is this element narrow”. Container queries answer that directly, in CSS, without needing to know anything about the editor state:
.my-block__wrap {
container-type: inline-size;
container-name: my-block;
}
/* Stack when the block itself is narrow, whatever the viewport is doing */
@container my-block (max-width: 480px) {
.my-block__layout {
flex-direction: column;
gap: var(--wp--preset--spacing--30, 16px);
}
}This is strictly better than the old approach for a reason that has nothing to do with the editor: it works in the front end too. A block placed in a narrow sidebar and the same block placed in a wide content column need different layouts, and viewport width cannot distinguish those two cases. Container width can.
If you need the measurement in JavaScript rather than CSS, measure the element:
import { useResizeObserver } from '@wordpress/compose';
function MyBlockEdit() {
const [ resizeListener, { width } ] = useResizeObserver();
const isNarrow = width !== null && width < 480;
return (
<div className={ isNarrow ? 'is-narrow' : 'is-wide' }>
{ resizeListener }
{ /* ... */ }
</div>
);
}useResizeObserver reports the actual rendered width of your element. It responds to the canvas being dragged, to the sidebar opening, and to the block being moved into a narrower column, none of which a device name captures.
If you were showing device-specific controls
Interfaces that showed different inspector controls per device were building a responsive editing experience on top of three states. That is what the new viewport state system in core is for, and rolling your own is now working against the platform rather than with it.
The @ prefix syntax for responsive states
Core has adopted an @ prefix convention for expressing responsive states in style data. If you generate or manipulate block styles programmatically, this is the shape to know.
The idea is that a style value can carry variants keyed by a state, and states are prefixed with @ to keep them distinguishable from ordinary style properties:
{
"styles": {
"spacing": {
"padding": {
"top": "2rem",
"@sm": { "top": "1rem" }
}
}
}
}Reading that: the base value applies, and the @sm variant applies within the small viewport state. The nesting keeps the responsive variant next to the value it varies rather than in a separate breakpoint block somewhere else in the file.
Two practical notes. First, aspect ratio and flex alignment controls now work inside viewport states, so a block can change its aspect ratio or alignment per state rather than only its spacing. Second, this is style data rather than CSS you author, so if you build interfaces that write into theme.json or block attributes, your writer needs to understand the prefixed keys or it will flatten them.
Treat the exact key names as still settling. The convention has been adopted in core, but a syntax introduced this close to a release is worth reading from the source rather than from a blog post, including this one, before you build a serialiser against it.
Layout changes shipping alongside
Three smaller changes land in the same area and are worth knowing because they alter rendered output.
Flex children with a fixed width are now genuinely fixed. Core applies flex-shrink: 0 to them. Previously, a child you had set to 200 pixels would still be compressed below that when the row ran out of room, which produced layouts that quietly disagreed with the width you specified. If you compensated for the old behaviour with your own CSS, that compensation is now double-applied and your element may be wider than intended.
Grid layouts gain a fill available space toggle, backed by an autoFit property. This is the auto-fit versus auto-fill distinction in CSS grid surfaced as an editor control: whether tracks stretch to consume leftover space or leave it empty. If you generate grid styles, expect the new property in block attributes.
Flex vertical alignment controls are in the inspector. Users can now set vertical alignment on flex layouts through the interface. If your block renders a flex layout and you were handling alignment through your own control, you may now have two controls fighting over the same declaration.
Testing this before it lands
Gutenberg 23.5 raises the minimum required WordPress version to 6.9, so check your test site is on a recent enough core before installing it:
wp core version
wp plugin install gutenberg --activate
wp plugin list --name=gutenberg --field=versionThen work through your blocks with the resize handle rather than with the old three states. The specific things to look at:
- Drag slowly through the widths where your layout changes. The old three-state model meant nobody ever saw the widths between the presets. Now they will. Layouts that were only ever checked at 360, 768 and 1200 pixels routinely break at 600.
- Check your block inside a narrow container, not just a narrow viewport. Place it in a sidebar or a two column layout and see whether it responds. If it only responds to viewport width, it will look wrong in a column.
- Look for anything that reads device type and confirm it still does something sensible when the canvas width and the device name disagree.
- Check fixed-width flex children against the new
flex-shrink: 0behaviour.
Turn on deprecation warnings while you do it, which will surface the hook if you are still calling it:
wp config set SCRIPT_DEBUG true --raw
wp config set WP_DEBUG true --rawA worked migration
Abstract advice is easy to agree with and hard to apply, so here is a complete block edit component before and after.
The starting point is a card block that switched between a horizontal and a stacked layout based on the editor’s device preview. It is representative of a lot of real code:
import { useResizeCanvas } from '@wordpress/block-editor';
import { useSelect } from '@wordpress/data';
import { store as editorStore } from '@wordpress/editor';
export default function Edit( { attributes } ) {
const deviceType = useSelect(
( select ) => select( editorStore ).getDeviceType(),
[]
);
const canvasStyles = useResizeCanvas( deviceType );
const isMobile = deviceType === 'Mobile';
return (
<div style={ canvasStyles }
className={ isMobile ? 'card card--stacked' : 'card card--row' }>
<figure className="card__media">{ /* ... */ }</figure>
<div className="card__body">{ attributes.text }</div>
</div>
);
}Three separate problems live in that snippet. It sizes the canvas, which is the editor’s job. It branches on a device name, which no longer implies a width. And the resulting class only applies in the editor, so the front end needs a parallel implementation that can drift.
The rewrite pushes the decision into CSS and deletes most of the JavaScript:
export default function Edit( { attributes } ) {
return (
<div className="card">
<figure className="card__media">{ /* ... */ }</figure>
<div className="card__body">{ attributes.text }</div>
</div>
);
}.card {
container-type: inline-size;
display: flex;
gap: var(--wp--preset--spacing--30, 16px);
}
@container (max-width: 420px) {
.card {
flex-direction: column;
}
}The component lost two imports, a store subscription and a branch. The behaviour improved, because the card now stacks when it is narrow rather than when the viewport is narrow, which means it does the right thing in a sidebar. And the same stylesheet drives the editor and the front end, so the two cannot disagree.
One caveat on the container query: container-type: inline-size establishes containment on the inline axis, which means the element can no longer be sized by its own contents in that direction. That is almost always fine for a layout wrapper and occasionally surprising for an element that was relying on intrinsic width. If a block collapses unexpectedly after you add it, that is the cause, and the fix is to move the containment to a wrapper rather than putting it on the element you are sizing.
Why the direction is right
It is worth stating plainly, because “the editor changed again” is an easy complaint and this particular change is one of the better ones.
Fixed device breakpoints were always a lie we agreed to tell. There is no such thing as tablet width. There is a continuous range of viewport sizes, and there are content-dependent points within that range where a given layout stops working. Those points differ per layout, which is exactly why a global three-state model could not express them.
The old model also made a category error by conflating viewport width with available space. A block does not care how wide the browser window is. It cares how much room it has been given, which depends on the column it sits in, whether a sidebar is open, and what its parent’s padding is. Container queries fixed that in CSS, and this change brings the editing experience into line.
The practical consequence for block authors is that responsive behaviour belongs in CSS driven by container size, not in JavaScript driven by editor state. That is a smaller amount of code, it works identically in the editor and the front end, and it does not break the next time the editor changes how previews work.
What this means for theme authors
Block authors have the clearer migration. Theme authors have the more interesting one, because a theme’s responsive behaviour lives in theme.json and stylesheets rather than in components.
The immediate question is what happens to your existing media queries. Nothing: they keep working, and a theme built on viewport breakpoints is not broken by this release. The editor changed how it previews widths, not how CSS resolves.
What changes is what your users will see while editing. Someone dragging the canvas continuously will pass through every width between your breakpoints, including the ranges you never looked at. Themes tested only at three widths tend to have visible gaps at the boundaries: a heading that wraps awkwardly between 700 and 780 pixels, a grid that leaves an orphan item, padding that collapses one step too early.
Those gaps existed before. Real visitors have always had viewports at every width. The difference is that they were previously invisible during editing and are now easy to find, which makes this a good moment to sweep them:
/* Breakpoint-driven: fine, still works, blind between the steps */
@media (min-width: 782px) {
.wp-block-columns { gap: 2rem; }
}
/* Fluid: covers the whole range without naming a device */
.wp-block-columns {
gap: clamp(1rem, 0.5rem + 2vw, 2rem);
}Fluid values using clamp() remove whole classes of boundary bug because there is no boundary. They are not a universal replacement, since some changes genuinely are discrete: a three column grid becoming one column is a jump, not an interpolation. Use fluid values for continuous properties like spacing and type size, and keep queries for structural changes.
If you are writing viewport states into theme.json, the @ prefix syntax described above is the shape core is standardising on, and it is worth matching rather than inventing a parallel convention.
Questions people are asking
Will my block break when 7.1 ships?
Only if it called useResizeCanvas() and used the result for layout. That code now receives nothing useful and whatever it sized will be unsized. Nothing throws, so test rather than wait for an error report.
Are the device preview buttons really gone?
The fixed three-state toggle is replaced by a continuously resizable preview. You can still reach specific widths, you simply are not limited to three of them. getDeviceType() continues to report a named state, so the concept has not vanished from the data layer.
Should I switch everything to container queries now?
For new work, yes. For existing blocks, migrate the ones whose layout actually depends on available space, and leave the rest. A block that looks the same at every width does not need a container query, and adding one is complexity without benefit.
Does this affect the front end or only the editor?
The hook and the preview are editor concerns. The layout changes are not: flex-shrink: 0 on fixed-width flex children, the grid autoFit property, and flex vertical alignment all change rendered output on the front end.
Is the @ prefix syntax stable enough to build against?
It has been adopted in core, which is a stronger signal than an experimental flag. It is also new. If you are writing a tool that serialises style data, read the current core implementation rather than trusting a summary, and expect to revisit it after 7.1 ships.
Can I still offer users a mobile preview button?
You can, and core still exposes named states through getDeviceType() and setDeviceType(), so a button that jumps the canvas to a narrow width is straightforward to build. Ask why you want it first. If the goal is a quick way to reach a common width, that is reasonable. If the goal is to restore the old three-state model because your block needs it, the block needs fixing rather than the editor.
What is the minimum WordPress version for Gutenberg 23.5?
6.9. If your test environment is older, install an earlier Gutenberg release or update core before testing.
Your checklist
- Grep for
useResizeCanvasand remove every call. - Where you were sizing the canvas, delete the code and let the editor own it.
- Where you were branching on device type for layout, move to container queries or
useResizeObserver. - Audit any code that maps a device name to a pixel width. That mapping is no longer reliable.
- Check fixed-width flex children for double-applied shrink compensation.
- Look for your own alignment controls competing with the new inspector controls.
- Test by dragging through widths, not by clicking presets.
- Test your block inside a narrow container as well as a narrow viewport.
This is the third change in 7.1 that fails silently rather than loudly, alongside the 40px component default and the icon colour inheritance change. All three are found by opening your interfaces and looking at them, which is an afternoon of work and the only reliable way to catch this class of problem before a customer does.





No comments yet