Ionic Open Graph Meta Tags: Add OG to Ionic Angular, React, and Vue
How to add og:title, og:image, og:description, and Twitter card meta tags in an Ionic app using Angular Meta service, React Helmet, or Vue useHead.
Ionic and OG tags: what you need to know
Ionic is a cross-platform UI toolkit that targets web, iOS, and Android from a single codebase. When deployed as a Progressive Web App (PWA) or website, Ionic apps are typically client-side rendered — meaning social crawlers won't execute JavaScript and will see an empty <head> unless you handle OG tags correctly.
The approach differs based on your framework: Angular uses the Meta service, React uses react-helmet-async, and Vue uses @vueuse/head. For social preview support, all three approaches need server-side rendering or static generation.
Angular Ionic: Meta service
Angular's built-in Meta and Title services let you set document head tags from any component. For crawler visibility, combine with Angular Universal (SSR) or Angular's static prerendering.
// src/app/pages/post/post.page.ts
import { Component, OnInit } from '@angular/core';
import { Meta, Title } from '@angular/platform-browser';
import { ActivatedRoute } from '@angular/router';
import { PostService } from '../../services/post.service';
@Component({
selector: 'app-post',
templateUrl: './post.page.html',
})
export class PostPage implements OnInit {
constructor(
private meta: Meta,
private title: Title,
private route: ActivatedRoute,
private postService: PostService,
) {}
async ngOnInit() {
const slug = this.route.snapshot.params['slug'];
const post = await this.postService.getPost(slug);
this.title.setTitle(`${post.title} | My Blog`);
// Open Graph
this.meta.updateTag({ property: 'og:type', content: 'article' });
this.meta.updateTag({ property: 'og:title', content: post.title });
this.meta.updateTag({ property: 'og:description', content: post.excerpt });
this.meta.updateTag({ property: 'og:image', content: post.ogImage });
this.meta.updateTag({ property: 'og:url', content: `https://yourdomain.com/post/${slug}` });
// Twitter Card
this.meta.updateTag({ name: 'twitter:card', content: 'summary_large_image' });
this.meta.updateTag({ name: 'twitter:title', content: post.title });
this.meta.updateTag({ name: 'twitter:description', content: post.excerpt });
this.meta.updateTag({ name: 'twitter:image', content: post.ogImage });
}
}For crawler visibility, add @nguniversal/express-engine to your project and run ng add @nguniversal/express-engine to enable Angular Universal SSR.
React Ionic: react-helmet-async
For Ionic React apps, use react-helmet-async to manage head tags. Wrap your app in HelmetProvider and add a Helmet component in each page.
npm install react-helmet-async
// src/App.tsx
import { HelmetProvider } from 'react-helmet-async';
const App: React.FC = () => (
<HelmetProvider>
<IonApp>
<IonReactRouter>
{/* your routes */}
</IonReactRouter>
</IonApp>
</HelmetProvider>
);
// src/pages/PostPage.tsx
import { Helmet } from 'react-helmet-async';
const PostPage: React.FC = () => {
const { slug } = useParams<{ slug: string }>();
const [post, setPost] = useState<Post | null>(null);
useEffect(() => {
fetchPost(slug).then(setPost);
}, [slug]);
if (!post) return <IonSpinner />;
return (
<IonPage>
<Helmet>
<title>{post.title} | My Blog</title>
<meta name="description" content={post.excerpt} />
<meta property="og:type" content="article" />
<meta property="og:title" content={post.title} />
<meta property="og:description" content={post.excerpt} />
<meta property="og:image" content={post.ogImage} />
<meta property="og:url" content={`https://yourdomain.com/post/${slug}`} />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content={post.title} />
<meta name="twitter:description" content={post.excerpt} />
<meta name="twitter:image" content={post.ogImage} />
</Helmet>
<IonContent>
<h1>{post.title}</h1>
</IonContent>
</IonPage>
);
};Vue Ionic: useHead / @vueuse/head
For Ionic Vue, use @vueuse/head (or @unhead/vue for Nuxt-compatible setups) to manage head tags reactively.
npm install @vueuse/head
// src/main.ts
import { createHead } from '@vueuse/head';
const head = createHead();
app.use(head);
// src/views/PostPage.vue
<script setup lang="ts">
import { useHead } from '@vueuse/head';
import { ref, onMounted } from 'vue';
const route = useRoute();
const post = ref<Post | null>(null);
onMounted(async () => {
post.value = await fetchPost(route.params.slug as string);
});
useHead(computed(() => ({
title: post.value ? `${post.value.title} | My Blog` : 'Loading...',
meta: post.value ? [
{ name: 'description', content: post.value.excerpt },
{ property: 'og:type', content: 'article' },
{ property: 'og:title', content: post.value.title },
{ property: 'og:description', content: post.value.excerpt },
{ property: 'og:image', content: post.value.ogImage },
{ property: 'og:url', content: `https://yourdomain.com/post/${route.params.slug}` },
{ name: 'twitter:card', content: 'summary_large_image' },
{ name: 'twitter:title', content: post.value.title },
{ name: 'twitter:image', content: post.value.ogImage },
] : [],
})));
</script>Capacitor deep links and OG tags
When sharing deep links from a Capacitor app (iOS/Android), users often share the app's web URL. If your Ionic web app has proper OG tags, those deep link previews will look great in iMessage, WhatsApp, and other messaging apps.
For native Capacitor apps, OG tags only apply to the web layer. The native app share sheet uses the OS's own preview — not OG tags. Focus your OG tag efforts on the web version of your Ionic app.
OG tag checklist for Ionic
- Use Angular Universal, React SSR, or Nuxt/SSG for crawler-visible OG tags.
- For Angular: use the
Metaservice per route. - For React: wrap app in
HelmetProviderand use per-pageHelmet. - For Vue: use
@vueuse/headwithuseHead(). - Set twitter:card to
summary_large_image. - Use absolute URLs for
og:image(1200×630 px).
Preview your Ionic app's social cards
Paste your Ionic web app URL into OGFixer to see how it previews on Twitter, LinkedIn, Discord, and Slack.