PHPackages                             mage2kishan/module-social-meta - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. mage2kishan/module-social-meta

ActiveMagento2-module[Utility &amp; Helpers](/categories/utility)

mage2kishan/module-social-meta
==============================

Panth Social Meta — OpenGraph and Twitter Card head tags for Magento 2, with CMS/product/category fallbacks.

1.0.7(1mo ago)014↓50%proprietaryPHPPHP ~8.1.0||~8.2.0||~8.3.0||~8.4.0

Since Apr 22Pushed yesterdayCompare

[ Source](https://github.com/mage2sk/module-social-meta)[ Packagist](https://packagist.org/packages/mage2kishan/module-social-meta)[ Docs](https://kishansavaliya.com)[ RSS](/packages/mage2kishan-module-social-meta/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (6)Versions (9)Used By (0)

Panth Social Meta — Open Graph + Twitter Card Tags for Magento 2 (Hyva + Luma)
==============================================================================

[](#panth-social-meta--open-graph--twitter-card-tags-for-magento-2-hyva--luma)

[![Magento 2.4.4 - 2.4.8](https://camo.githubusercontent.com/079c832211eed4f9451ebe264e3865f825b0f9f31b041cbf03676c6e254535d4/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d6167656e746f2d322e342e342532302d2d253230322e342e382d6f72616e67653f6c6f676f3d6d6167656e746f266c6f676f436f6c6f723d7768697465)](https://magento.com)[![PHP 8.1 - 8.4](https://camo.githubusercontent.com/56b3cce18841623e2cbed2ebf09b06be1be8807e99e6e054a89d304ab4790b8e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e312532302d2d253230382e342d626c75653f6c6f676f3d706870266c6f676f436f6c6f723d7768697465)](https://php.net)[![Hyva Compatible](https://camo.githubusercontent.com/14365166e02048aff917dd0a015feecdae28499fbde05fa17abd4f7821ea1139/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f487976612d436f6d70617469626c652d3134623861363f6c6f676f3d616c70696e65646f746a73266c6f676f436f6c6f723d7768697465)](https://hyva.io)![Luma Compatible](https://camo.githubusercontent.com/3c1945ee121ef64870a6f3583c91ffdfb4d8ffc35809e7ce34ca549a357e1ded/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c756d612d436f6d70617469626c652d6f72616e6765)[![Packagist](https://camo.githubusercontent.com/40b6797efa0f64682cbcfbbe633d9bbc481826a4acf79747928b85b0ea66cc64/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5061636b61676973742d6d616765326b697368616e2532466d6f64756c652d2d736f6369616c2d2d6d6574612d6f72616e67653f6c6f676f3d7061636b6167697374266c6f676f436f6c6f723d7768697465)](https://packagist.org/packages/mage2kishan/module-social-meta)[![Upwork Top Rated Plus](https://camo.githubusercontent.com/6f72584179420c41ed90432fd2579a4ed36199d4229e8181d20f353c1c4ee4eb/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5570776f726b2d546f702532305261746564253230506c75732d3134613830303f6c6f676f3d7570776f726b266c6f676f436f6c6f723d7768697465)](https://www.upwork.com/freelancers/~016dd1767321100e21)[![Website](https://camo.githubusercontent.com/f1ae86d28e2b505aee60f240d3e5508e390b0a8dc7a9b7ecf1b450fad862053f/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f576562736974652d6b697368616e736176616c6979612e636f6d2d304439343838)](https://kishansavaliya.com)[![Get a Quote](https://camo.githubusercontent.com/0b6c02cc1ad00f11bf1b0164a9998734bd716473db36cc2a5c1517e3d3578d1b/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4765742532306125323051756f74652d46726565253230457374696d6174652d444332363236)](https://kishansavaliya.com/get-quote)

 [![Panth Social Meta — free Open Graph and Twitter Card extension for Magento 2, built by Kishan Savaliya (Panth Infotech), Top Rated Plus on Upwork.](docs/images/hero-banner.png)](docs/images/hero-banner.png)

> **Out of the box Magento emits five og:* tags on product pages and zero on categories or CMS pages — and on Hyva the core `product/view/opengraph/general.phtml` template happily duplicates whatever a theme, a third-party SEO module, or a manual layout snippet has already added.*\* The visible result is a Facebook preview that picks the wrong image, a Twitter card that omits the `@handle`, and view-source output with entity-encoded `&#x40;` where you wrote `@yourstore`. **Panth Social Meta** fixes the stack end-to-end: one resolver computes `og:*` and `twitter:*` from the current entity (product / category / CMS) with progressive image / title / description fallbacks, an observer on `layout_generate_blocks_after` strips Magento and Hyva's native OG blocks so duplicates can never render, and the templates use `escapeUrl()` for URL-valued attributes and `escapeHtml()` (ENT\_QUOTES | ENT\_HTML5) — not Zend Escaper — for text-valued ones so `@yourstore` stays readable while still being XSS-safe. Five store-scoped admin fields cover the entire configuration surface. Zero JS, zero theme overrides, identical output on Hyva and Luma.

Product pages additionally emit `product:price:amount`, `product:price:currency`, `product:availability` (derived from `$product->isSalable()` → `instock` | `oos`) and `product:brand` (from the `manufacturer` attribute) so Facebook Shop and every major product-feed ingester pick up pricing and stock state without a separate feed plugin. `og:locale` tracks each store's `general/locale/code`, so a UK store view ships `en_GB` while the US store view ships `en_US` from the same codebase.

---

Need Custom Magento 2 Development?
----------------------------------

[](#need-custom-magento-2-development)

 [ ![Get a Free Quote](https://camo.githubusercontent.com/eac8c45d21cff8b139ddc392325f3bd6c8266a6f3d7b23f15131c958f3d3c8d0/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f476574253230612532304672656525323051756f74652532302545322538362539322d5265706c7925323077697468696e2532303234253230686f7572732d4443323632363f7374796c653d666f722d7468652d6261646765) ](https://kishansavaliya.com/get-quote)

### Kishan Savaliya

[](#kishan-savaliya)

**Top Rated Plus on Upwork**

[![Hire on Upwork](https://camo.githubusercontent.com/b69353d3c6e192f4d03cc36bb8883612004e32f54dd2dbcc1e700dd791acd875/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f486972652532306f6e2532305570776f726b2d546f702532305261746564253230506c75732d3134613830303f7374796c653d666f722d7468652d6261646765266c6f676f3d7570776f726b266c6f676f436f6c6f723d7768697465)](https://www.upwork.com/freelancers/~016dd1767321100e21)

### Panth Infotech Agency

[](#panth-infotech-agency)

[![Visit Agency](https://camo.githubusercontent.com/bbf04bdd2aff502082508568ec42ace3a7475c98756f596e2013056c89726ed6/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f56697369742532304167656e63792d50616e7468253230496e666f746563682d3134613830303f7374796c653d666f722d7468652d6261646765266c6f676f3d7570776f726b266c6f676f436f6c6f723d7768697465)](https://www.upwork.com/agencies/1881421506131960778/)

---

Table of Contents
-----------------

[](#table-of-contents)

- [Preview](#preview)
- [Features](#features)
- [How It Works](#how-it-works)
- [Compatibility](#compatibility)
- [Installation](#installation)
- [Verify](#verify)
- [Configuration](#configuration)
- [Storefront Rendering](#storefront-rendering)
- [Testing Your Social Tags](#testing-your-social-tags)
- [Troubleshooting](#troubleshooting)
- [Support](#support)

---

Preview
-------

[](#preview)

### Admin

[](#admin)

**System configuration** — **Stores → Configuration → Panth Extensions → Social Meta**. Five store-scoped fields: Enable Open Graph Tags, Enable Twitter Card Tags, Twitter Card Type (`summary` | `summary_large_image`), Twitter Site Handle (`@yourstore`), and Default OG Image (uploaded under `media/panth_seo/og/`). Every field is `[store view]`-scoped so a multi-store install can ship a different handle, card type and fallback image per storefront.

[![Admin configuration](docs/images/admin-config.png)](docs/images/admin-config.png)

---

Features
--------

[](#features)

FeatureDescription**Single-emission OG + Twitter**`Observer\Social\RemoveNativeOgObserver` runs on `layout_generate_blocks_after` and unsets every native OG block it can find — the four well-known names (`opengraph.general`, `opengraph.product`, `opengraph.category`, `opengraph.cms`) plus anything matching `opengraph` (substring) or `og.` (prefix). The module's own blocks (prefixed `panth_social_meta.`) are explicitly skipped. Net result: exactly one `og:title`, one `og:image`, one `og:url` per page — never a duplicate pair from a theme + module collision.**Disable = silence**When `og_enabled = No`, the observer still runs so Magento core's `product/view/opengraph/general.phtml` (which ships 5 `og:*` tags on every PDP) does not silently leak through. Flipping the toggle off means zero `og:*` on the storefront, which is what the admin label actually implies.**Entity-aware resolver**`Model\Social\OpenGraphResolver::resolve()` detects the current entity via `current_product` / `current_category` in the Magento registry. Products get `og:type=product` plus `product:price:*`, `product:availability`, `product:brand`. Categories and CMS pages get `og:type=website`. Title / description / image / URL each have their own progressive fallback chain so the tags never blank out.**Title fallback chain**Product: `meta_title` → `name`. Category: `meta_title` → `name`. CMS / other: `PageConfig->getTitle()` (set by the CMS controller from the page's own meta\_title) → store name.**Description fallback chain**Product: `meta_description` → `short_description`. Category: `meta_description` → `description`. CMS / other: `PageConfig->getDescription()` → `design/head/default_description`. Every description is truncated at 200 characters using a single-character ellipsis `…` (not three dots) so Facebook's preview card renders cleanly.**Image fallback chain**Product image → category image → first in-stock product image in that category → admin-configured Default OG Image (`panth_social_meta/social/default_og_image`, stored under `media/panth_seo/og/`) → store logo (`design/header/logo_src`) → Magento's product placeholder. Never blank unless the store has no logo and no placeholder either.**URL canonicalisation**`og:url` uses the entity's own URL model (`$product->getProductUrl(false)` / `$category->getUrl()`), query string stripped. CMS / other pages use `$store->getCurrentUrl(false)` with `?` and `#` trimmed. Rendered through `escapeUrl()` so `:` and `/` stay intact in view-source.**`og:site_name` prefers brand over store label**Reads `general/store_information/name` first, falling back to `$store->getName()`. So a merchant with "Acme Store" in Store Information ships that as the site name even if the Magento store view label is still the factory default "Default Store View".**`og:locale` per store view**Reads `general/locale/code` at store scope and emits it verbatim (`en_US`, `en_GB`, `fr_FR`, …). Multi-store installs get correct per-region `og:locale` with no admin work.**Product stock state**`product:availability` is derived from `$product->isSalable()` — `instock` when salable, `oos` when not. Flipping stock status in the admin immediately changes the emitted tag on the next uncached render, so Facebook Shop and catalog ingesters see accurate stock state.**Product brand**`product:brand` reads the `manufacturer` attribute's dropdown label via `getAttributeText('manufacturer')`. Optional — only emitted when the attribute is set.**Product price tags**`product:price:amount` and `product:price:currency` on PDP when the final price is positive. Price is formatted with exactly two decimals and a period separator, matching the OpenGraph product vocab.**Twitter Card wraps OG**`TwitterCardResolver` reuses the OG output — `twitter:title`, `twitter:description`, `twitter:image` all mirror their `og:*` counterparts, `twitter:card` is the admin-configured type, and `twitter:site` carries the configured handle (emitted only when non-empty). Turn OG off and Twitter auto-switches off; turn OG on and Twitter on independently.**Escape-safe rendering**URL-valued attributes (`og:url`, `og:image`, `og:image:secure_url`, `og:video`, `og:audio`, `twitter:image`, `twitter:image:src`, `twitter:player`) go through `escapeUrl()` which preserves `:` and `/`. Text-valued attributes go through `escapeHtml()` which uses `htmlspecialchars()` with `ENT\_QUOTES**EAV OG attributes on product + category**`Setup\Patch\Data\AddOgAttributes` installs `og_title`, `og_description`, `og_image` on every product and category attribute set, under the "Search Engine Optimization" fieldset. Populate them per-entity when you want to override what the meta\_title / meta\_description fallback would emit.**Theme-agnostic head rendering**`view/frontend/layout/default.xml` attaches both head blocks to `head.additional`. Templates are plain PHP `` output — no Alpine directives, no RequireJS, no `x-magento-init`. Identical output on Hyva, Luma, Breeze, custom themes.**Fully cacheable**Both head blocks declare `cacheable="true"`. Output ends up in full-page cache alongside the rest of `` and costs nothing per-request after the first uncached render. The observer fires only during layout generation, not on cached hits.---

How It Works
------------

[](#how-it-works)

Four cooperating pieces, one request:

1. **`Observer\Social\RemoveNativeOgObserver`** runs on `layout_generate_blocks_after` in the frontend area. Before any block is rendered it walks the layout tree twice — once by well-known block names (`opengraph.general`, `opengraph.product`, `opengraph.category`, `opengraph.cms`), once by pattern (`opengraph` substring, `og.` prefix) — and `unsetElement()`s each match. This module's own blocks start with `panth_social_meta.` and are skipped by name, so only native Magento / Hyva / third-party OG blocks disappear. The observer runs unconditionally: when a merchant disables the module's OG output they expect zero `og:*` on the storefront, not Magento core's 5-tag native template filling the vacuum.
2. **`Model\Social\OpenGraphResolver::resolve()`** is the read path. It detects the current entity from the Magento registry, walks the title / description / image / URL fallback chains, pulls `og:site_name` from `general/store_information/name`, pulls `og:locale` from `general/locale/code`, and — when the entity is a product — adds `product:price:amount`, `product:price:currency`, `product:availability` (from `isSalable()`) and `product:brand` (from the `manufacturer` attribute). Returns a flat `['og:property' => 'value']` array with empty values filtered out.
3. **`Model\Social\TwitterCardResolver::resolve()`** wraps the OG result. `twitter:title` / `twitter:description` / `twitter:image` mirror the OG equivalents so the two preview cards always agree. `twitter:card` is the admin-configured type, `twitter:site` is the configured handle (omitted when empty). Returns an empty array if the OG resolver returns empty — so disabling OG silently disables Twitter too.
4. **The two phtml templates** (`view/frontend/templates/head/opengraph.phtml` + `.../twittercard.phtml`) iterate the resolved array and emit one `` tag per entry. URL-valued attribute names (hardcoded allowlist) go through `$escaper->escapeUrl()`, everything else goes through `$escaper->escapeHtml()`. The property / name value itself is always escaped with `escapeHtml()` because the allowlist is fixed to ASCII. When the resolver returns empty (module disabled, no entity, etc.) the template early-returns and writes nothing.

The EAV patch (`Setup\Patch\Data\AddOgAttributes`) is a one-shot install that runs during `setup:upgrade` — it adds `og_title`, `og_description`, `og_image` to every product and category attribute set, under the "Search Engine Optimization" group. The attributes are read by the resolver via the standard `getData()` path when set.

---

Compatibility
-------------

[](#compatibility)

RequirementSupportedMagento Open Source2.4.4, 2.4.5, 2.4.6, 2.4.7, 2.4.8Adobe Commerce2.4.4 — 2.4.8PHP8.1, 8.2, 8.3, 8.4Hyva Theme1.0+ (fully compatible — no theme overrides)Luma ThemeNative supportPanth Core^1.0 (installed automatically)---

Installation
------------

[](#installation)

```
composer require mage2kishan/module-social-meta
bin/magento module:enable Panth_Core Panth_SocialMeta
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flush
```

---

Verify
------

[](#verify)

```
bin/magento module:status Panth_SocialMeta
# Module is enabled

# Any product page should emit exactly one og:* block + one twitter:* block
curl -ks https://hyva.test/example-product.html | grep -oE ']*>'
#
#
#
#
#
#
#

# Luma store view — identical output from a different domain
curl -ks https://luma.test/example-product.html | grep -oE ']+>'
#

# Confirm no duplicates — this count should always be 1 per property per page
curl -ks https://hyva.test/example-product.html | grep -c '

      Panth\SocialMeta\ViewModel\OpenGraph

      Panth\SocialMeta\ViewModel\TwitterCard

```

### Product page (PDP)

[](#product-page-pdp)

```

```

### Category page

[](#category-page)

```

```

### CMS page (About Us, Contact, Shipping Policy, …)

[](#cms-page-about-us-contact-shipping-policy-)

```

```

### Luma store view — identical resolver output, different host

[](#luma-store-view--identical-resolver-output-different-host)

```

```

Because the resolver is driven entirely by store-scope config (`general/locale/code`, `general/store_information/name`, `panth_social_meta/social/*`) and the registry-detected entity, two store views on the same install produce correct-per-store output without any per-store layout XML. The EAV fallback (`og_title` / `og_description` / `og_image`) is also store-scoped via the standard ScopedAttributeInterface.

### What **disable** looks like

[](#what-disable-looks-like)

When both master switches are off, the page ships zero social meta — not a fallback to Magento core's native OG template:

```
curl -ks https://hyva.test/example-product.html | grep -c ' `${m.getAttribute('property') || m.getAttribute('name')} → ${m.content}`)

// Duplicate detection — any count > 1 is a bug
const names = [...document.querySelectorAll('meta[property], meta[name]')]
  .map(m => m.getAttribute('property') || m.getAttribute('name'))
  .filter(n => n.startsWith('og:') || n.startsWith('twitter:') || n.startsWith('product:'));
const dupes = names.filter((n, i) => names.indexOf(n) !== i);
console.log('Duplicates:', dupes.length ? dupes : 'none ✓');
```

### CI smoke test

[](#ci-smoke-test)

A minimal curl-based test you can wire into deploy:

```
for url in / /example-product.html /men/tops-men.html /about-us; do
  og=$(curl -ks "https://hyva.test${url}" | grep -c '' | sort -u
# Should show exactly one line
```

If two different lines appear, identify the offending module (`bin/magento module:status` + `grep -r "og:title" vendor//`) and either (a) open an issue with the offending module to adopt a predictable block name (`opengraph.*` or `og.*`), or (b) add its block name to `Observer\Social\RemoveNativeOgObserver::NATIVE_OG_BLOCKS` and PR upstream.

### `twitter:site` shows `&#x40;yourstore` instead of `@yourstore`

[](#twittersite-shows-x40yourstore-instead-of-yourstore)

Your theme is shipping an override of `view/frontend/templates/head/twittercard.phtml` that uses `$escaper->escapeHtmlAttr()` instead of `$escaper->escapeHtml()`. Magento's `escapeHtmlAttr()` runs Zend\\Escaper which aggressively encodes every non-ASCII-safe byte, so the `@` sign becomes `&#x40;`. Social scrapers decode entities so the handle still **works**, but it looks wrong in view-source and some linters flag it. Fix: remove the theme override, or mirror the module's escape strategy (`escapeUrl()` for URL-valued names, `escapeHtml()` for everything else).

### `product:availability` always `instock` even for out-of-stock products

[](#productavailability-always-instock-even-for-out-of-stock-products)

The resolver reads `$product->isSalable()`. If this returns true for an out-of-stock product, you have a stock configuration issue, not a social meta one — check *Stores → Configuration → Catalog → Inventory → Stock Options → Display Out of Stock Products* and the product's own stock status. Confirm by loading the product in a bin/magento script:

```
php -r '
require __DIR__ . "/app/bootstrap.php";
$bs = \Magento\Framework\App\Bootstrap::create(BP, []);
$om = $bs->getObjectManager();
$om->get(\Magento\Framework\App\State::class)->setAreaCode("frontend");
$p = $om->create(\Magento\Catalog\Model\ProductRepository::class)->get("example-product");
var_dump($p->isSalable());
'
```

If this prints `true` but the frontend shows Out of Stock, the resolver is reporting `isSalable()` correctly and the storefront display is driven by stock inventory — bring the two in line via the product's Stock Status.

### `og:site_name` = "Default Store View"

[](#ogsite_name--default-store-view)

Nothing has been set in *Stores → Configuration → General → Store Information → Store Name*. The resolver prefers that value (the merchant-facing brand name) and only falls back to the Magento store view label when it's empty. Set the Store Name per store view and re-flush caches.

### `og:image` returning an absolute URL for the placeholder even though a Default OG Image is uploaded

[](#ogimage-returning-an-absolute-url-for-the-placeholder-even-though-a-default-og-image-is-uploaded)

The uploaded file must live under `media/panth_seo/og/` — the backend model `Magento\Config\Model\Config\Backend\Image` stores it there automatically when uploaded via the admin form. If you moved the file by hand, move it back or re-upload via the admin. The resolver also rejects values containing `..`, backslashes, leading `/`, or null bytes as a path-traversal guard.

### Tests pass but Facebook's preview still shows the old image

[](#tests-pass-but-facebooks-preview-still-shows-the-old-image)

Facebook aggressively caches OG data. Open the [Sharing Debugger](https://developers.facebook.com/tools/debug/), paste the URL, and click **Scrape Again**. Twitter/X and LinkedIn have equivalent controls. The resolver does not cache OG data itself — the stale preview is server-side on the scraper, not the store.

---

Support
-------

[](#support)

- **Agency:** [Panth Infotech on Upwork](https://www.upwork.com/agencies/1881421506131960778/)
- **Direct:** [kishansavaliya.com](https://kishansavaliya.com) — [Get a free quote](https://kishansavaliya.com/get-quote)

###  Health Score

44

—

FairBetter than 90% of packages

Maintenance97

Actively maintained with recent releases

Popularity8

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity56

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

8

Last Release

32d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/343e344aa298f189db888b32d62f9202d31ced1a5ea23411850a63dc4a30299c?d=identicon)[kishansavaliya](/maintainers/kishansavaliya)

---

Top Contributors

[![KishanSavaliya](https://avatars.githubusercontent.com/u/16853223?v=4)](https://github.com/KishanSavaliya "KishanSavaliya (10 commits)")

---

Tags

socialseotwitter cardopengraphmagento2magento2 modulepanth

### Embed Badge

![Health badge](/badges/mage2kishan-module-social-meta/health.svg)

```
[![Health](https://phpackages.com/badges/mage2kishan-module-social-meta/health.svg)](https://phpackages.com/packages/mage2kishan-module-social-meta)
```

###  Alternatives

[run-as-root/magento2-prometheus-exporter

Magento2 Prometheus Exporter

68353.9k](/packages/run-as-root-magento2-prometheus-exporter)[mollie/magento2

Mollie Payment Module for Magento 2

1131.8M12](/packages/mollie-magento2)[loki/magento2-components

Core module for defining Alpine.js components with advanced AJAX features

1010.0k22](/packages/loki-magento2-components)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
