diff --git a/docs/reference-guides/core-blocks.md b/docs/reference-guides/core-blocks.md
index a23ed2f9934e27..d722604edda4cf 100644
--- a/docs/reference-guides/core-blocks.md
+++ b/docs/reference-guides/core-blocks.md
@@ -982,6 +982,15 @@ Edit the different global regions of your site, like the header, footer, sidebar
- **Supports:** align, interactivity (clientNavigation), ~~html~~, ~~renaming~~, ~~reusable~~
- **Attributes:** area, slug, tagName, theme
+## Term Count
+
+Displays the post count of a taxonomy term. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/term-count))
+
+- **Name:** core/term-count
+- **Category:** theme
+- **Supports:** color (background, gradients, text), interactivity (clientNavigation), spacing (padding), typography (fontSize, lineHeight), ~~html~~
+- **Attributes:** bracketType
+
## Term Description
Display the description of categories, tags and custom taxonomies when viewing an archive. ([Source](https://github.com/WordPress/gutenberg/tree/trunk/packages/block-library/src/term-description))
diff --git a/lib/blocks.php b/lib/blocks.php
index 236011b03abd0c..ca39b2e55bf4a9 100644
--- a/lib/blocks.php
+++ b/lib/blocks.php
@@ -124,6 +124,7 @@ function gutenberg_reregister_core_block_types() {
'table-of-contents.php' => 'core/table-of-contents',
'tag-cloud.php' => 'core/tag-cloud',
'template-part.php' => 'core/template-part',
+ 'term-count.php' => 'core/term-count',
'term-description.php' => 'core/term-description',
'term-name.php' => 'core/term-name',
'terms-query.php' => 'core/terms-query',
diff --git a/packages/block-library/src/index.js b/packages/block-library/src/index.js
index e56c6c6b00d32c..ef0b1b2ffde21c 100644
--- a/packages/block-library/src/index.js
+++ b/packages/block-library/src/index.js
@@ -128,6 +128,7 @@ import * as table from './table';
import * as tableOfContents from './table-of-contents';
import * as tagCloud from './tag-cloud';
import * as templatePart from './template-part';
+import * as termCount from './term-count';
import * as termDescription from './term-description';
import * as termName from './term-name';
import * as termsQuery from './terms-query';
@@ -250,6 +251,7 @@ const getAllBlocks = () => {
tableOfContents,
homeLink,
logInOut,
+ termCount,
termDescription,
termName,
queryTitle,
diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss
index feb673ae47a477..3e40e89b8e0083 100644
--- a/packages/block-library/src/style.scss
+++ b/packages/block-library/src/style.scss
@@ -70,6 +70,7 @@
@import "./tag-cloud/style.scss";
@import "./table/style.scss";
@import "./table-of-contents/style.scss";
+@import "./term-count/style.scss";
@import "./term-description/style.scss";
@import "./term-name/style.scss";
@import "./term-template/style.scss";
diff --git a/packages/block-library/src/term-count/block.json b/packages/block-library/src/term-count/block.json
new file mode 100644
index 00000000000000..c4de1e61f8d1f5
--- /dev/null
+++ b/packages/block-library/src/term-count/block.json
@@ -0,0 +1,58 @@
+{
+ "$schema": "https://schemas.wp.org/trunk/block.json",
+ "apiVersion": 3,
+ "name": "core/term-count",
+ "title": "Term Count",
+ "category": "theme",
+ "description": "Displays the post count of a taxonomy term.",
+ "textdomain": "default",
+ "usesContext": [ "termId", "taxonomy" ],
+ "attributes": {
+ "bracketType": {
+ "type": "string",
+ "enum": [ "none", "round", "square", "curly", "angle" ],
+ "default": "round"
+ }
+ },
+ "supports": {
+ "html": false,
+ "color": {
+ "gradients": true,
+ "__experimentalDefaultControls": {
+ "background": true,
+ "text": true
+ }
+ },
+ "spacing": {
+ "padding": true
+ },
+ "typography": {
+ "fontSize": true,
+ "lineHeight": true,
+ "__experimentalFontFamily": true,
+ "__experimentalFontWeight": true,
+ "__experimentalFontStyle": true,
+ "__experimentalTextTransform": true,
+ "__experimentalTextDecoration": true,
+ "__experimentalLetterSpacing": true,
+ "__experimentalDefaultControls": {
+ "fontSize": true
+ }
+ },
+ "interactivity": {
+ "clientNavigation": true
+ },
+ "__experimentalBorder": {
+ "radius": true,
+ "color": true,
+ "width": true,
+ "style": true,
+ "__experimentalDefaultControls": {
+ "color": true,
+ "width": true,
+ "style": true
+ }
+ }
+ },
+ "style": "wp-block-term-count"
+}
diff --git a/packages/block-library/src/term-count/edit.js b/packages/block-library/src/term-count/edit.js
new file mode 100644
index 00000000000000..19d90d630d2165
--- /dev/null
+++ b/packages/block-library/src/term-count/edit.js
@@ -0,0 +1,91 @@
+/**
+ * WordPress dependencies
+ */
+import { __ } from '@wordpress/i18n';
+import { useBlockProps, BlockControls } from '@wordpress/block-editor';
+import { ToolbarDropdownMenu } from '@wordpress/components';
+
+/**
+ * Internal dependencies
+ */
+import {
+ bareNumber,
+ numberInParenthesis,
+ numberInSquareBrackets,
+ numberInCurlyBrackets,
+ numberInAngleBrackets,
+} from './icons';
+import { useTermCount } from './use-term-count';
+
+const BRACKET_TYPES = {
+ none: { label: __( 'No brackets' ), icon: bareNumber },
+ round: {
+ label: __( 'Round brackets' ),
+ icon: numberInParenthesis,
+ before: '(',
+ after: ')',
+ },
+ square: {
+ label: __( 'Square brackets' ),
+ icon: numberInSquareBrackets,
+ before: '[',
+ after: ']',
+ },
+ curly: {
+ label: __( 'Curly brackets' ),
+ icon: numberInCurlyBrackets,
+ before: '{',
+ after: '}',
+ },
+ angle: {
+ label: __( 'Angle brackets' ),
+ icon: numberInAngleBrackets,
+ before: '<',
+ after: '>',
+ },
+};
+
+export default function TermCountEdit( {
+ attributes,
+ setAttributes,
+ context: { termId, taxonomy },
+} ) {
+ const { bracketType } = attributes;
+ const term = useTermCount( termId, taxonomy );
+
+ const termCount = term?.termCount || 0;
+
+ const blockProps = useBlockProps();
+
+ const bracketTypeControls = Object.entries( BRACKET_TYPES ).map(
+ ( [ type, { label, icon } ] ) => ( {
+ role: 'menuitemradio',
+ title: label,
+ isActive: bracketType === type,
+ icon,
+ onClick: () => {
+ setAttributes( { bracketType: type } );
+ },
+ } )
+ );
+
+ const formatTermCount = ( count, type ) => {
+ const { before = '', after = '' } = BRACKET_TYPES[ type ] || {};
+ return `${ before }${ count }${ after }`;
+ };
+
+ return (
+ <>
+
+
+
+
+ { formatTermCount( termCount, bracketType ) }
+
+ >
+ );
+}
diff --git a/packages/block-library/src/term-count/icons.js b/packages/block-library/src/term-count/icons.js
new file mode 100644
index 00000000000000..877acfe90e11b0
--- /dev/null
+++ b/packages/block-library/src/term-count/icons.js
@@ -0,0 +1,34 @@
+/**
+ * WordPress dependencies
+ */
+import { SVG, Path } from '@wordpress/components';
+
+export const bareNumber = (
+
+);
+
+export const numberInParenthesis = (
+
+);
+
+export const numberInSquareBrackets = (
+
+);
+
+export const numberInCurlyBrackets = (
+
+);
+
+export const numberInAngleBrackets = (
+
+);
diff --git a/packages/block-library/src/term-count/index.js b/packages/block-library/src/term-count/index.js
new file mode 100644
index 00000000000000..24481c946586d1
--- /dev/null
+++ b/packages/block-library/src/term-count/index.js
@@ -0,0 +1,21 @@
+/**
+ * WordPress dependencies
+ */
+import { termCount as icon } from '@wordpress/icons';
+
+/**
+ * Internal dependencies
+ */
+import initBlock from '../utils/init-block';
+import metadata from './block.json';
+import edit from './edit';
+
+const { name } = metadata;
+export { metadata, name };
+
+export const settings = {
+ icon,
+ edit,
+};
+
+export const init = () => initBlock( { name, metadata, settings } );
diff --git a/packages/block-library/src/term-count/index.php b/packages/block-library/src/term-count/index.php
new file mode 100644
index 00000000000000..9794896524eb43
--- /dev/null
+++ b/packages/block-library/src/term-count/index.php
@@ -0,0 +1,80 @@
+context['termId'] ) && isset( $block->context['taxonomy'] ) ) {
+ $term = get_term( $block->context['termId'], $block->context['taxonomy'] );
+ } else {
+ $term = get_queried_object();
+ if ( ! $term instanceof WP_Term ) {
+ $term = null;
+ }
+ }
+
+ if ( ! $term || is_wp_error( $term ) ) {
+ return '';
+ }
+
+ $term_count = $term->count;
+
+ // Format the term count based on bracket type.
+ switch ( $attributes['bracketType'] ) {
+ case 'none':
+ // No formatting needed.
+ break;
+ case 'round':
+ $term_count = "({$term_count})";
+ break;
+ case 'square':
+ $term_count = "[{$term_count}]";
+ break;
+ case 'curly':
+ $term_count = "{{$term_count}}";
+ break;
+ case 'angle':
+ $term_count = "<{$term_count}>";
+ break;
+ default:
+ // Default to no formatting for unknown types.
+ break;
+ }
+
+ $wrapper_attributes = get_block_wrapper_attributes();
+
+ return sprintf(
+ '%2$s
',
+ $wrapper_attributes,
+ $term_count
+ );
+}
+
+/**
+ * Registers the `core/term-count` block on the server.
+ *
+ * @since 6.9.0
+ */
+function register_block_core_term_count() {
+ register_block_type_from_metadata(
+ __DIR__ . '/term-count',
+ array(
+ 'render_callback' => 'render_block_core_term_count',
+ )
+ );
+}
+add_action( 'init', 'register_block_core_term_count' );
diff --git a/packages/block-library/src/term-count/init.js b/packages/block-library/src/term-count/init.js
new file mode 100644
index 00000000000000..79f0492c2cb2f8
--- /dev/null
+++ b/packages/block-library/src/term-count/init.js
@@ -0,0 +1,6 @@
+/**
+ * Internal dependencies
+ */
+import { init } from './';
+
+export default init();
diff --git a/packages/block-library/src/term-count/style.scss b/packages/block-library/src/term-count/style.scss
new file mode 100644
index 00000000000000..ab992d5e1b94bc
--- /dev/null
+++ b/packages/block-library/src/term-count/style.scss
@@ -0,0 +1,4 @@
+.wp-block-term-count {
+ // This block has customizable padding, border-box makes that more predictable.
+ box-sizing: border-box;
+}
diff --git a/packages/block-library/src/term-count/use-term-count.js b/packages/block-library/src/term-count/use-term-count.js
new file mode 100644
index 00000000000000..959d9b51d0b4e2
--- /dev/null
+++ b/packages/block-library/src/term-count/use-term-count.js
@@ -0,0 +1,101 @@
+/**
+ * WordPress dependencies
+ */
+import { store as coreStore, useEntityProp } from '@wordpress/core-data';
+import { useSelect } from '@wordpress/data';
+
+/**
+ * Hook to fetch term count based on context or fallback to template parsing.
+ *
+ * This hook prioritizes context-provided termId and taxonomy, but falls back to
+ * template-based detection when no context is available.
+ *
+ * @param {string|number} termId The term ID from context
+ * @param {string} taxonomy The taxonomy name from context
+ */
+export function useTermCount( termId, taxonomy ) {
+ const [ count ] = useEntityProp( 'taxonomy', taxonomy, 'count', termId );
+
+ // Fallback approach: Parse template slug when no context is available.
+ const templateBasedData = useTemplateBasedTermData();
+
+ const hasContext = Boolean( termId && taxonomy );
+
+ return {
+ hasContext,
+ termCount: hasContext ? count || '' : templateBasedData,
+ };
+}
+
+/**
+ * Fallback hook to fetch term data from template context (backward compatibility).
+ * This maintains the same logic as the original implementation for cases where
+ * no termId/taxonomy context is provided.
+ */
+function useTemplateBasedTermData() {
+ const templateSlug = useSelect( ( select ) => {
+ // Access core/editor by string to avoid @wordpress/editor dependency.
+ // eslint-disable-next-line @wordpress/data-no-store-string-literals
+ const { getCurrentPostId, getCurrentPostType, getCurrentTemplateId } =
+ select( 'core/editor' );
+ const currentPostType = getCurrentPostType();
+ const templateId =
+ getCurrentTemplateId() ||
+ ( currentPostType === 'wp_template' ? getCurrentPostId() : null );
+
+ return templateId
+ ? select( coreStore ).getEditedEntityRecord(
+ 'postType',
+ 'wp_template',
+ templateId
+ )?.slug
+ : null;
+ }, [] );
+
+ const taxonomyMatches = templateSlug?.match(
+ /^(category|tag|taxonomy-([^-]+))$|^(((category|tag)|taxonomy-([^-]+))-(.+))$/
+ );
+
+ let taxonomy;
+ let termSlug;
+
+ if ( taxonomyMatches ) {
+ // If it's for all taxonomies of a type (e.g., category, tag).
+ if ( taxonomyMatches[ 1 ] ) {
+ taxonomy = taxonomyMatches[ 2 ]
+ ? taxonomyMatches[ 2 ]
+ : taxonomyMatches[ 1 ];
+ }
+ // If it's for a specific term (e.g., category-news, tag-featured).
+ else if ( taxonomyMatches[ 3 ] ) {
+ taxonomy = taxonomyMatches[ 6 ]
+ ? taxonomyMatches[ 6 ]
+ : taxonomyMatches[ 4 ];
+ termSlug = taxonomyMatches[ 7 ];
+ }
+
+ taxonomy = taxonomy === 'tag' ? 'post_tag' : taxonomy;
+ }
+
+ return useSelect(
+ ( select ) => {
+ if ( ! taxonomy || ! termSlug ) {
+ return '';
+ }
+
+ const { getEntityRecords } = select( coreStore );
+
+ const termRecords = getEntityRecords( 'taxonomy', taxonomy, {
+ slug: termSlug,
+ per_page: 1,
+ } );
+
+ if ( termRecords && termRecords[ 0 ] ) {
+ return termRecords[ 0 ].count || '';
+ }
+
+ return '';
+ },
+ [ taxonomy, termSlug ]
+ );
+}
diff --git a/packages/icons/src/library/term-count.svg b/packages/icons/src/library/term-count.svg
new file mode 100644
index 00000000000000..8b333407a02ef4
--- /dev/null
+++ b/packages/icons/src/library/term-count.svg
@@ -0,0 +1,4 @@
+
diff --git a/test/integration/fixtures/blocks/core__term-count.html b/test/integration/fixtures/blocks/core__term-count.html
new file mode 100644
index 00000000000000..6b4f1fb433ec23
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__term-count.html
@@ -0,0 +1 @@
+
diff --git a/test/integration/fixtures/blocks/core__term-count.json b/test/integration/fixtures/blocks/core__term-count.json
new file mode 100644
index 00000000000000..d1a5dcdea81557
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__term-count.json
@@ -0,0 +1,10 @@
+[
+ {
+ "name": "core/term-count",
+ "isValid": true,
+ "attributes": {
+ "bracketType": "round"
+ },
+ "innerBlocks": []
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__term-count.parsed.json b/test/integration/fixtures/blocks/core__term-count.parsed.json
new file mode 100644
index 00000000000000..0e17d9047d41c0
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__term-count.parsed.json
@@ -0,0 +1,9 @@
+[
+ {
+ "blockName": "core/term-count",
+ "attrs": {},
+ "innerBlocks": [],
+ "innerHTML": "",
+ "innerContent": []
+ }
+]
diff --git a/test/integration/fixtures/blocks/core__term-count.serialized.html b/test/integration/fixtures/blocks/core__term-count.serialized.html
new file mode 100644
index 00000000000000..6b4f1fb433ec23
--- /dev/null
+++ b/test/integration/fixtures/blocks/core__term-count.serialized.html
@@ -0,0 +1 @@
+