No, you don't, and for very good reasons that might not seem obvious at first. In essence your block needs to be available everywhere and to be able to handle that, even if it does this by not displaying anything. Just because you're in the site editor doesn't mean the block won't appear inside a post.
But before that makes sense there is missing information you are unaware of. I'll need to explain why, and how Core itself tackles this for block such as post content etc.
in block.json. How can I allow the block being used only on pages and posts but not in FSE?
This is a contradiction and a paradox. This hypothetical scenario is simply impossible in a block theme.
This is because:
- a homepage template will contain posts which themselves are composed of blocks
- a post context itself may be provided by an FSE template or template part, or even another block, e.g. a query block
- posts themselves can contain template parts
- posts can have custom FSE templates
- reusable blocks, widgets, block based nav, etc
So the distinction between FSE and post blocks does not exist, based on a common misunderstanding. The way you've thought of it is natural given how Gutenberg has been presented as a post editor though.
Use Block Context Instead
Instead, you need to do what all the post specific blocks in core do and rely on context:
https://developer.wordpress.org/block-editor/reference-guides/block-api/block-context/
Lets use the official post title block as an example. We can see that its block.json
uses:
"usesContext": [ "postId", "postType", "queryId" ],
That requested context is then pulled into the javascript Edit component as a prop, the same way attributes are:
export default function PostTitleEdit( {
attributes: { level, textAlign, isLink, rel, linkTarget },
setAttributes,
context: { postType, postId, queryId },
} ) {
The title block then does some internal logic and pulls in some values via the useEntity
hook to make some decisions, and the context values it was given provide the post type and ID.
Finally, when deciding if it should render components, it does this:
let titleElement = (
<TagName { ...blockProps }>{ __( 'Post Title' ) }</TagName>
);
if ( postType && postId ) {
titleElement =
Here it's showing Post Title
as a placeholder, then replacing that placeholder if there's a post ID/type available, and it does this again further down the file before doing {titleElement}
at the end of its edit component. As a result, it shows the post information in both the post editor and in the site editor, while still providing the settings UI.
We also see in index.php
when rendering server side it gets the context via the $block
parameter:
function render_block_core_post_title( $attributes, $content, $block ) {
if ( ! isset( $block->context['postId'] ) ) {
return '';
}
Here it short circuits and exits with ''
if there is no post ID.
It's important to note here that you do not know where a site template part is going to be used, or where a block may appear. You don't have to restrict your block to only appearing in the post editor, and if you do you'll probably end up with a broken post when that post shows up in the site editor with missing or unloaded blocks that the editor complains about.
By requesting and using context, you can be sure your block is always rendering the current post. Some people try to build work arounds and fetch the post ID from the store and save it as an attribute, but that means their block can't be used in context dependent situations reliably, e.g. inside query blocks.
Generally, refer to what the official Gutenberg repo does for these things. Core blocks tend to be kept up to late, sometimes bleeding edge, and should be taken as best practice in most cases.
Here's the official post title block for a closer view:
https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/post-title
You'll find other core blocks including those for the site editor here:
https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/