AdonisJS Open Graph Tags: Add OG to Your Edge.js Templates
Add og:title, og:image, og:description and Twitter card tags to AdonisJS apps using Edge.js view templates — with practical copy-paste examples.
AdonisJS + Edge.js: server-rendered OG tags
AdonisJS uses Edge.js as its template engine. Since Edge templates are rendered on the server, social crawlers receive fully-rendered HTML with all OG meta tags — no JavaScript execution required. This makes AdonisJS a great choice for pages that need correct social previews.
Layout template with OG tags
Create a base layout that includes OG tag slots with sensible defaults:
{{-- resources/views/layouts/main.edge --}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>{{ ogTitle ?? 'My AdonisJS App' }}</title>
{{-- Open Graph --}}
<meta property="og:title" content="{{ ogTitle ?? 'My AdonisJS App' }}" />
<meta property="og:description" content="{{ ogDescription ?? 'Default description.' }}" />
<meta property="og:image" content="{{ ogImage ?? 'https://yourdomain.com/og/default.png' }}" />
<meta property="og:url" content="{{ ogUrl ?? request.completeUrl() }}" />
<meta property="og:type" content="{{ ogType ?? 'website' }}" />
{{-- Twitter Card --}}
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="{{ ogTitle ?? 'My AdonisJS App' }}" />
<meta name="twitter:description" content="{{ ogDescription ?? 'Default description.' }}" />
<meta name="twitter:image" content="{{ ogImage ?? 'https://yourdomain.com/og/default.png' }}" />
</head>
<body>
@!section('content')
</body>
</html>Controller: passing OG data to views
// app/controllers/posts_controller.ts
import type { HttpContext } from '@adonisjs/core/http'
import Post from '#models/post'
export default class PostsController {
async show({ params, view }: HttpContext) {
const post = await Post.findByOrFail('slug', params.slug)
return view.render('posts/show', {
post,
ogTitle: post.title,
ogDescription: post.excerpt,
ogImage: `https://yourdomain.com/og/${post.slug}.png`,
ogUrl: `https://yourdomain.com/blog/${post.slug}`,
ogType: 'article',
})
}
async index({ view }: HttpContext) {
const posts = await Post.query().orderBy('created_at', 'desc')
return view.render('posts/index', {
posts,
ogTitle: 'Blog — My AdonisJS App',
ogDescription: 'Read our latest articles.',
ogImage: 'https://yourdomain.com/og/blog.png',
ogUrl: 'https://yourdomain.com/blog',
})
}
}Page template using the layout
{{-- resources/views/posts/show.edge --}}
@layout('layouts/main')
@section('content')
<article>
<h1>{{ post.title }}</h1>
<p>{{ post.excerpt }}</p>
<div>{{{ post.html }}}</div>
</article>
@endEdge.js component for reusable OG tags
Extract OG tags into a reusable Edge component:
{{-- resources/views/components/og-tags.edge --}}
@component()
@prop('title')
@prop('description')
@prop('image')
@prop('url')
@prop('type', 'website')
<meta property="og:title" content="{{ title }}" />
<meta property="og:description" content="{{ description }}" />
<meta property="og:image" content="{{ image }}" />
<meta property="og:url" content="{{ url }}" />
<meta property="og:type" content="{{ type }}" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="{{ title }}" />
<meta name="twitter:description" content="{{ description }}" />
<meta name="twitter:image" content="{{ image }}" />
@end
{{-- Usage in layout: --}}
<head>
<title>{{ ogTitle }}</title>
@!component('components/og-tags', {
title: ogTitle,
description: ogDescription,
image: ogImage,
url: ogUrl,
type: ogType
})
</head>AdonisJS middleware for default OG
Set global OG defaults via middleware so every route has a fallback:
// app/middleware/og_defaults_middleware.ts
import type { HttpContext } from '@adonisjs/core/http'
import type { NextFn } from '@adonisjs/core/types/http'
export default class OgDefaultsMiddleware {
async handle({ request, view }: HttpContext, next: NextFn) {
view.share({
ogTitle: 'My AdonisJS App',
ogDescription: 'Default site description.',
ogImage: 'https://yourdomain.com/og/default.png',
ogUrl: request.completeUrl(),
ogType: 'website',
})
return next()
}
}
// start/kernel.ts
router.use([
() => import('#middleware/og_defaults_middleware'),
])
// Now any controller can override specific values:
// return view.render('page', { ogTitle: 'Custom Title' })Verify your AdonisJS OG tags
After deploying, paste your URL into OGFixer to preview how Twitter, Slack, Discord, and LinkedIn will render your links.