How to set a different meta tag in the head depending on the page

I would like to create some sort of dynamic open graph tags. I will briefly explain the use case to make it obvious: I've got a webapp to generate action figures. I want people to share their action figures in URLs like this (https://tufiguradeaccion.com/guest-figure/8258a638-0416-4b62-a720-1e116d004d5a) and for them to see the Open Graph tags with the action figure as the image. I've been researching and I've found that there is no support for SSG. I understand ... I was wondering if there is any workaround or something I can do. I hear the approach is to use "react-helmet-async" but I don't know how to start ... is there any sample repo where I can get inspiration from? Thanks in advance. Further details: - Wasp version: 0.16.3 - Platform: MacOS
Tu figura de acción
🦾 Crea tu figura de acción personalizada en segundos
15 Replies
kapa.ai
kapa.ai6d ago
Hi @kessius85. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- You're right that Wasp doesn't currently have built-in support for SSG or dynamic meta tags in the head section of your app. However, there is a workaround using React Helmet that's recommended by the Wasp team. Current State of SEO in Wasp Wasp allows you to set static meta tags in the head section of your main.wasp file, but these are global and not dynamic per page GitHub Issue #1910. For your use case of having different Open Graph tags for each action figure page, you'll need a client-side solution. Recommended Workaround As you mentioned, the recommended approach is to use react-helmet-async. Here's how you can implement it: 1. Install react-helmet-async in your project 2. Use Wasp's rootComponent to set up the Helmet provider 3. Create dynamic meta tags in your specific pages/components The Wasp team specifically recommends this approach for your exact use case:
"In the meantime, solution might be https://github.com/nfl/react-helmet -> it allows you to set head directly from React. I see it is not currently maintained but allegedly this fork is https://github.com/staylor/react-helmet-async. And to set it in just one place, you can use Wasp's rootComponent https://wasp-lang.dev/docs/project/client-config#defining-a-common-layout." Discord Support Thread
Example Implementation For inspiration, you can look at CoverLetterGPT, an open-source Wasp app that has good SEO despite not using SSR. The developer mentions:
"Wasp allows you to add OG tags in the head property of the main.wasp file" Discord Support Thread
You can check out their implementation here: CoverLetterGPT GitHub Future Plans The Wasp team is aware of this limitation and has open issues to address both SSR/SSG and dynamic meta tags: - Issue #911 for SSR/SSG - Issue #1786 for environment-specific values in meta tags Additional SEO Notes It's worth noting that even without SSR, you can still achieve good SEO results with Wasp:
"A lot of hype around needing SSR for good SEO is exaggerated... Google is very good at scraping websites, and even does so with JS activated." Discord Support Thread
The CoverLetterGPT app mentioned above ranks well in Google searches despite not using SSR or a separate landing page. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @kessius85, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
kessius85
kessius85OP5d ago
I've been looking at how to implement "react-helmet-async" and didn't manage ... I will tell you what I did and you tell me what's missing. I did: * install react-helmet-async * (Layout.jsx) import { HelmetProvider } from "react-helmet-async"; // and added the <HelmetProvider> tag * (DynamicPage.jsx) import { Helmet } from 'react-helmet-async'; // and added the <Helmet> tag and the dynamic tags inside Summary: We installed the react-helmet-async library to handle dynamic metadata in the React component. We modified the Layout.jsx component to wrap the entire app with the HelmetProvider, which allows child components to use Helmet to manipulate head tags. We implemented Helmet in the GuestFigureDetail.jsx component, where: We imported the Helmet component from react-helmet-async We created variables for the dynamic metadata based on the current figure We added the dynamic OG tags inside the Helmet component in the render We included metadata for Facebook and Twitter This is how my "main.wasp" client part looks like:
client: {
rootComponent: import { Layout } from "@src/Layout.jsx",
},
client: {
rootComponent: import { Layout } from "@src/Layout.jsx",
},
Two questiosn: 1. Do I need to remove the "head" tags from the main.wasp to make the react-helmet-async work? 2. What am I missing? @kapa.ai can you help me?
kapa.ai
kapa.ai5d ago
Let me check the knowledge base for you again. One moment! :books:
MEE6
MEE65d ago
Wohooo @kessius85, you just became a Waspeteer level 1!
kapa.ai
kapa.ai5d ago
---------------------- I'd be happy to help you with your Wasp project! However, your question is quite general. Based on our previous conversation, I understand you're working on implementing dynamic Open Graph tags for your action figure web app. To help you more effectively, could you please specify what aspect you need help with? For example: 1. Setting up react-helmet-async in your Wasp project 2. Implementing dynamic OG tags for your action figure pages 3. Understanding how to use Wasp's rootComponent for this purpose 4. Something else related to your Wasp implementation With more details, I can provide you with more specific guidance for your use case. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @kessius85, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
NEROX
NEROX5d ago
Hey @kessius85 what meta-tags are you trying to show?
kessius85
kessius85OP5d ago
I'm trying to set up the OG tags (Open Graph)
kessius85
kessius85OP5d ago
I'm specially interested in making this viralizable, so if I share the URL the small image is displayed
Tu figura de acción
🦾 Crea tu figura de acción personalizada en segundos
kessius85
kessius85OP5d ago
@kapa.ai I'm interested to know how to setup react-helmet-async
kapa.ai
kapa.ai5d ago
Let me check the knowledge base for you again. One moment! :books:
kessius85
kessius85OP5d ago
@NEROX do you know if there is any sample project or reference that I look at?
kapa.ai
kapa.ai5d ago
---------------------- I'd be happy to help you set up react-helmet-async in your Wasp project. Based on the knowledge sources, here's how you can implement it: Setting up react-helmet-async in Wasp 1. First, install the package in your Wasp project:
npm install react-helmet-async
npm install react-helmet-async
2. Create a root component that will wrap your entire application with the HelmetProvider. You can do this by creating a file like src/RootComponent.jsx (or .tsx if using TypeScript):
import React from 'react';
import { HelmetProvider } from 'react-helmet-async';

const RootComponent = ({ children }) => {
return (
<HelmetProvider>
{children}
</HelmetProvider>
);
};

export default RootComponent;
import React from 'react';
import { HelmetProvider } from 'react-helmet-async';

const RootComponent = ({ children }) => {
return (
<HelmetProvider>
{children}
</HelmetProvider>
);
};

export default RootComponent;
3. Configure this as your root component in your main.wasp file:
app MyApp {
title: "My Action Figure App",
// other app config...
client: {
rootComponent: import { default as RootComponent } from "@src/RootComponent"
}
}
app MyApp {
title: "My Action Figure App",
// other app config...
client: {
rootComponent: import { default as RootComponent } from "@src/RootComponent"
}
}
4. Now you can use the Helmet component in any of your page components to set dynamic meta tags. For example, in your action figure detail page:
import React from 'react';
import { Helmet } from 'react-helmet-async';

const ActionFigureDetailPage = ({ figure }) => {
return (
<div>
<Helmet>
<title>{figure.name} | Tu Figura de Acción</title>
<meta property="og:title" content={`${figure.name} | Tu Figura de Acción`} />
<meta property="og:description" content={`Check out this custom action figure: ${figure.name}`} />
<meta property="og:image" content={figure.imageUrl} />
<meta property="og:url" content={`<https://tufiguradeaccion.com/guest-figure/${figure.id}`}> />
<meta property="og:type" content="website" />
</Helmet>

{/* Your page content */}
</div>
);
};
import React from 'react';
import { Helmet } from 'react-helmet-async';

const ActionFigureDetailPage = ({ figure }) => {
return (
<div>
<Helmet>
<title>{figure.name} | Tu Figura de Acción</title>
<meta property="og:title" content={`${figure.name} | Tu Figura de Acción`} />
<meta property="og:description" content={`Check out this custom action figure: ${figure.name}`} />
<meta property="og:image" content={figure.imageUrl} />
<meta property="og:url" content={`<https://tufiguradeaccion.com/guest-figure/${figure.id}`}> />
<meta property="og:type" content="website" />
</Helmet>

{/* Your page content */}
</div>
);
};
This approach allows you to dynamically set different meta tags for each page based on the data you have for that specific action figure. The Helmet component will inject these tags into the document head at runtime. The Wasp team specifically recommends this approach as a workaround until they implement native support for dynamic meta tags in the future Discord Support Thread. Is there anything specific about the implementation you'd like me to explain further? ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ---------------------- Sorry @kessius85, I seem to be having a bit of trouble. Don't worry though, I've pinged my support and they will try to fix me up! :technologist: Perhaps try again in 15mins?
NEROX
NEROX5d ago
hm I understand, it's currently not possible as it's SPA. react-helmet-async is a good solution to handle SEO but not perfect: - The head of main.wasp is rendered only in the initial HTML, and then React only manipulates the DOM, but generally (with the exception of Google and some other) bots/crawlers (Linkedin, Facebook, Twitter, etc. ) usually only read the initial HTML, not the DOM modified by JS. - react-helmet-async only changes the head in the browser DOM, but not in the HTML that the bots/crawlers receive, unless you use SSR (Server Side Rendering), which Wasp doesn't support yet. So, even if you change the <meta property="og:image" ...> with Helmet, the bots only see the one in the initial HTML (the one in main.wasp).
NEROX
NEROX5d ago
One solution (external to Wasp) is to use some pre-render service (prerender as an example) or any Open-Source. They act as middleware/proxy between your app and the bots/crawlers. When a bot (Google, Facebook, Twitter, etc.) requests a page from your SPA, Prerender launches a headless browser (like Puppeteer/Chrome), loads your app, waits for the JS to run and the DOM to update (including React Helmet changes), and then returns the rendered HTML to the bot. For normal users, the app works as usual (SPA). But as I said at the beginning, as of today it's not possible with Wasp.
Prerender
Prerender Seamlessly Renders JavaScript for Faster Indexing
Get higher rankings by serving search engine crawlers a static HTML version of your JavaScript website without compromising your customers’ experience.
kessius85
kessius85OP4d ago
thanks a lot, @NEROX 🙂 ! appreciate your explanation ❤️ !

Did you find this page helpful?