Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Opengraph Laravel Package

euskadi31/opengraph

euskadi31/opengraph is a PHP library for generating Open Graph meta tags, helping developers add social-sharing metadata to pages with a simple, object-oriented API and clean, reusable tag definitions.

Deep Wiki
Context7

Getting Started

Install and confirm the API surface first. The package is framework-agnostic, so there is no service provider, no config file, and no facade — which is the whole point and also the first thing to internalise.

composer require euskadi31/opengraph

Before writing a line of application code, open vendor/euskadi31/opengraph/src/ and read it. The entire library is a handful of small classes (an Open Graph container plus value objects for images, video, audio, article metadata). Five minutes there will save you an hour of guessing at method names — the README on Packagist is thinner than the source, and the last release was December 2022, so treat the source as the documentation.

First use case. A single page that needs a shareable preview: a blog post or product page. Build the graph in the controller, hand it to the view, render inside <head>.

// app/Http/Controllers/PostController.php
use OpenGraph\OpenGraph;

public function show(Post $post)
{
    $og = new OpenGraph();
    $og->setTitle($post->title);
    $og->setDescription(Str::limit(strip_tags($post->excerpt), 160));
    $og->setUrl(route('posts.show', $post));
    $og->setImage(Storage::disk('public')->url($post->hero_path));
    $og->setType('article');

    return view('posts.show', compact('post', 'og'));
}
{{-- resources/views/layouts/app.blade.php --}}
<head>
    <meta charset="utf-8">
    <title>@yield('title')</title>
    {!! $og->render() !!}   {{-- note the unescaped braces --}}
</head>

Verify immediately. Don't trust the rendered string by eye. Paste the deployed URL into Facebook's Sharing Debugger and the LinkedIn Post Inspector on day one. Those two tools will tell you in ten seconds whether you have relative image URLs, a bad og:type, or missing og:image dimensions — the three failure modes that account for most broken previews.

Implementation Patterns

Wrap the package behind your own class. With 92 stars and a two-year-old release, the package is a dependency you want to be able to swap. A thin App\Support\OpenGraph\Graph that exposes title(), description(), image(), url(), type() and delegates to the vendor class costs an hour and means a future fork (or a hand-rolled Blade component) is a one-file change. It also gives you a place to put your defaults and sanitisation rules.

Populate defaults with a view composer, not every controller. Site name, default type, default fallback image, locale, and a twitter:card fallback all belong in one place:

View::composer('layouts.app', function ($view) {
    $view->with('og', $view->og ?? app(GraphFactory::class)->make());
});

The factory sets og:site_name from config('app.name'), og:type to website, og:locale to str_replace('-', '_', app()->getLocale()), and a default image. Controllers then only override what's page-specific — which is usually just title, description, image, and URL.

Use a Blade component for the model-driven case. A <x-open-graph :model="$post" /> component with a small convention (title → og:title, summary/excerpt → og:description, slug → og:url) keeps controllers clean and makes the article-specific tags easy:

@props(['model'])
@php($og = app(GraphFactory::class)->forModel($model))
@if($model instanceof \App\Models\Post)
    @php($og->setType('article'))
    {{-- article:published_time, article:author, article:section, article:tag --}}
@endif

Article tags are where euskadi31/opengraph earns its keep — article:published_time (ISO 8601), article:author, article:tag (one per tag) are tedious to hand-write and easy to get subtly wrong.

Absolutise every URL. og:image and og:url must be fully qualified. Use route(), url()->current(), or Storage::disk('public')->url(...) — never asset() on a path you haven't confirmed is absolute in production behind a proxy. If you're on S3/CloudFront, set ASSET_URL and verify it in the debugger.

Cache the rendered output for hot pages. Rendering is cheap, but if you're generating tags for a model with eager-loaded relations (author, tags, category), wrapping the whole block in Cache::remember("og:post:{$post->id}:{$post->updated_at}", 3600, fn () => $og->render()) removes it from the request entirely and invalidates naturally on updated_at.

Keep Twitter/X separate. This package emits og:* only. twitter:card, twitter:image, and twitter:site must be written by hand in your layout — and on X, an explicit twitter:image overrides og:image, so it's worth setting it even though it duplicates the value.

Testing. Use $response->assertSee('og:title', false) — the false disables escaping, since the tags are raw HTML. Assert on the presence of the property, not on the whole tag string, so a whitespace change in the package doesn't break your suite.

Gotchas and Tips

Blade will escape your tags. {{ $og->render() }} produces &lt;meta…. Use {!! !!} — and then grep the output for stray & in titles, because you've just taken responsibility for escaping that the package may or may not do for you. Test with a title containing ", &, and an emoji.

Duplicate tags silently win or lose depending on the scraper. If your layout already has a hardcoded <meta property="og:title"> for the homepage, adding dynamic tags produces two of them. Facebook generally takes the last one; Slack and iMessage vary. Keep exactly one source of truth per property.

og:type is not decorative. article without article:published_time is a warning; product without product:price:amount is worse than omitting the type. If you can't populate the type-specific properties, use website.

Image specs are non-negotiable. Minimum 200×200, recommended 1200×630 (1.91:1), under 8 MB, JPEG/PNG/GIF/WebP. Add og:image:width, og:image:height, and og:image:alt — platforms render faster and more accurately with explicit dimensions, and alt text is an accessibility requirement on some surfaces now. A signed S3 URL that expires will produce a broken preview days after you tested it; serve hero images from public paths only.

Locale format is en_US, not en-US. Use underscores, and set og:locale:alternate for each other language the page exists in. Getting this wrong quietly breaks locale-targeted sharing.

Position matters more than you'd think. Put the Open Graph block at the very top of <head>, before scripts and stylesheets. Some crawlers and link-preview bots truncate at a fixed byte count, and Laravel's Vite tags plus a couple of analytics snippets eat into that budget fast.

Stale package, real compatibility risk. Before upgrading PHP or Laravel, check that the package's type declarations and any deprecated dynamic-property use survive. Pin it ("euskadi31/opengraph": "1.0.*") and keep the wrapper class so you can reimplement render() as a ten-line Blade partial if it ever breaks. There is no active maintenance to lean on.

Debugging workflow. When a preview won't update: (1) view-source the deployed page and confirm the tags are actually there; (2) confirm URLs are absolute; (3) re-scrape in the Sharing Debugger — the platforms cache aggressively and will happily show you a week-old image; (4) check that your CDN/bot-protection isn't serving a challenge page to facebookexternalhit or Slackbot, which is a common cause of "no preview at all" on Laravel apps behind Cloudflare or a WAF.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
coleus/frame
thtg88/journalism
filagora/filagora-ai-forums
rlacerda83/query-parser
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings