Skip to content
Reactions
Reactions
Universal reactions for MODX 3 — likes, GitHub-style sets, tops and trending on any object
  1. Extras
  2. Reactions
  3. Snippets
  4. ReactionsSchema

SEO: ReactionsSchema

The ReactionsSchema snippet outputs JSON-LD AggregateRating markup for search engines.

Parameters

ParameterDefaultDescription
classmodResourceObject class
objectcurrent resource IDObject ID
contextcurrent context keyMODX context
toPlaceholder(empty)Placeholder name instead of direct output

Calculation logic

The snippet reads like and dislike counts (also counts up/down):

ConditionOutput
Both likes and dislikesratingValue = 1…5 via 1 + 4 × (likes / voted), ratingCount = likes + dislikes
Likes only, no dislikesratingCount = likes (no ratingValue)
No reactionsEmpty string, no tag

Sample JSON-LD with 80 likes and 20 dislikes:

json
{
  "@context": "https://schema.org",
  "@type": "AggregateRating",
  "ratingValue": 4.2,
  "ratingCount": 100,
  "bestRating": 5,
  "worstRating": 1
}

Examples

JSON-LD appears only when like/up or dislike/down is non-zero. Emoji-only types such as love without like/dislike do not produce markup.

Resource with votes

In <head> or before </body>:

modx
[[!ReactionsSchema?
    &class=`modResource`
    &object=`[[*id]]`
    &context=`web`
]]
fenom
{raw ('!ReactionsSchema' | snippet : [
    'class'   => 'modResource',
    'object'  => $_modx->resource.id,
    'context' => 'web',
])}

Empty object — empty output

No like/dislike → the snippet returns an empty string (handy in a template condition):

modx
[[!ReactionsSchema?
    &class=`modResource`
    &object=`999999001`
    &context=`web`
    &toPlaceholder=`rx.schema`
]]
[[+rx.schema:notempty=`[[+rx.schema]]`]]
fenom
{set $schema = '!ReactionsSchema' | snippet : [
    'class'   => 'modResource',
    'object'  => 999999001,
    'context' => 'web',
]}
{if $schema}{raw $schema}{/if}

miniShop3 product

modx
[[!ReactionsSchema?
    &class=`msProduct`
    &object=`[[*id]]`
    &context=`web`
]]
fenom
{raw ('!ReactionsSchema' | snippet : [
    'class'   => 'msProduct',
    'object'  => $_modx->resource.id,
    'context' => 'web',
])}

Into a placeholder

modx
[[!ReactionsSchema?
    &object=`[[*id]]`
    &context=`web`
    &toPlaceholder=`rx.schema`
]]
[[+rx.schema]]
fenom
{'!ReactionsSchema' | snippet : [
    'object'  => $_modx->resource.id,
    'context' => 'web',
    'toPlaceholder' => 'rx.schema',
]}
{raw ($_modx->getPlaceholder('rx.schema'))}

Relation to the main Schema.org type

AggregateRating must sit inside an object with @type (Article, Product, BlogPosting, and so on). The snippet outputs only the rating block.

Option 1: wrap it in your own snippet or template.

html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "[[*pagetitle]]",
  "aggregateRating": {
    "ratingValue": 4.2,
    "ratingCount": 100,
    "bestRating": 5,
    "worstRating": 1
  }
}
</script>
html
<script type="application/ld+json">
{
  "@context": "https://schema.org",
  "@type": "Article",
  "headline": "{$_modx->resource.pagetitle | escape}",
  "aggregateRating": {
    "ratingValue": 4.2,
    "ratingCount": 100,
    "bestRating": 5,
    "worstRating": 1
  }
}
</script>

Option 2: output ReactionsSchema next to the main markup. Google merges blocks on the same page, but a nested structure is more reliable.

Integration with SEO plugins

If an SEO plugin already generates JSON-LD, turn off duplicate AggregateRating in one of the sources. Check the result in Rich Results Test.

When markup does not appear

  • The object has no reactions.
  • All reactions are types other than like/dislike (for example only love in the github set), with no paired up/down. The snippet outputs ratingCount only when there are likes and no dislikes.

For SEO with the GitHub set, remember that ratingValue is computed only from like/dislike.