mirror of
https://github.com/wasp-lang/open-saas.git
synced 2025-04-11 05:19:04 +02:00
Merge c984a062d78cb56eba907ed046022a14c8343f1b into fd509b201070e486b4d05067167d80ad8d93f25e
This commit is contained in:
commit
c129a3de54
@ -30,7 +30,149 @@ app SaaSTemplate {
|
||||
|
||||
Change the above highlighted meta tags to match your app. Wasp will inject these tags into the HTML of your `index.html` file, which is the Landing Page (`app/src/client/landing-page/LandingPage.tsx`), in this case.
|
||||
|
||||
This means you **do not** need to rely on a separate app or framework to serve your landing page for SEO purposes.
|
||||
## Other Pages Meta Tags
|
||||
|
||||
React Helmet Async is a React library that allows you to modify `<head>` directly from your React component, in a dynamic fashion. Therefore, it can also be used to set meta tags.
|
||||
|
||||
:::note
|
||||
Since Wasp is SPA, React Helmet Async updates `<head>` via client-side JS after initial serve, meaning that web crawlers that don't evaluate JS won't pick up the modifications to the `<head>` you did.
|
||||
:::
|
||||
|
||||
|
||||
The first step is to install it:
|
||||
|
||||
```bash
|
||||
# Using npm
|
||||
npm install react-helmet-async
|
||||
```
|
||||
|
||||
Next, you need to wrap your main App component (`app/src/client/App.tsx`) with `HelmetProvider`:
|
||||
|
||||
```jsx
|
||||
//Add the react-helmet-async import
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
|
||||
//Wrap the main App component
|
||||
export default function App() {
|
||||
return (
|
||||
<HelmetProvider>
|
||||
<>
|
||||
<div className='min-h-screen dark:text-white dark:bg-boxdark-2'>
|
||||
{isAdminDashboard ? (
|
||||
<Outlet />
|
||||
) : (
|
||||
{shouldDisplayAppNavBar && <AppNavBar />}
|
||||
<div className='mx-auto max-w-7xl sm:px-6 lg:px-8'>
|
||||
<Outlet />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<CookieConsentBanner />
|
||||
</>
|
||||
</HelmetProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Now, you can set page-specific meta tags in your React components.
|
||||
|
||||
```jsx {6-33)
|
||||
//...
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
|
||||
export function MyCustomPage() {
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<title>My Custom Page Title</title>
|
||||
<meta
|
||||
name='description'
|
||||
content='This is the meta description of my page.'
|
||||
/>
|
||||
<link rel='canonical' href='https://example.com/my-custom-page' />
|
||||
<meta name="robots" content="noindex, nofollow" />
|
||||
|
||||
|
||||
{/* Open Graph / Facebook */}
|
||||
<meta property='og:type' content='website' />
|
||||
<meta property='og:url' content='https://example.com/my-custom-page' />
|
||||
<meta property='og:title' content='My Custom Page Title' />
|
||||
<meta
|
||||
property='og:description'
|
||||
content='This is the Open Graph description of my page.'
|
||||
/>
|
||||
<meta property='og:image' content='https://example.com/my-custom-page-og-image.jpg' />
|
||||
|
||||
{/* Twitter */}
|
||||
<meta name='twitter:card' content='summary_large_image' />
|
||||
<meta name='twitter:url' content='https://example.com/my-custom-page' />
|
||||
<meta name='twitter:title' content='My Custom Page Title' />
|
||||
<meta
|
||||
name='twitter:description'
|
||||
content='This is the Twitter description of my page.'
|
||||
/>
|
||||
<meta name='twitter:image' content='https://example.com/my-custom-page-twitter-image.jpg' />
|
||||
</Helmet>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
:::tip[Good SEO practice]
|
||||
There are certain pages that it is good SEO practice not to index, for example:
|
||||
|
||||
- Pages that do not add value (login, signup, password reset, ....).
|
||||
- Legal pages: Privacy Policy, Cookies Policy, Terms and Conditions.
|
||||
- Situational pages (e.g. page made for a specific campaign).
|
||||
:::
|
||||
|
||||
## Structured data and Schema markup
|
||||
|
||||
:::note[Tip]
|
||||
Crawlers do all the work of analyzing and understanding the content of your pages, and they will thank you if you include structured data to help them understand what your content is about!🤗.
|
||||
:::
|
||||
|
||||
You can add structured data for each page.
|
||||
|
||||
```jsx {14-22}
|
||||
//...
|
||||
import { Helmet, HelmetProvider } from 'react-helmet-async';
|
||||
|
||||
export function MyCustomPage() {
|
||||
return (
|
||||
<div>
|
||||
<Helmet>
|
||||
<title>My Custom Page Title</title>
|
||||
<meta
|
||||
name='description'
|
||||
content='This is the meta description of my page.'/>
|
||||
<link rel='canonical' href='https://example.com/my-custom-page' />
|
||||
//...
|
||||
|
||||
<script type='application/ld+json'>{`
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
"@type": "SoftwareApplication",
|
||||
"name": "title",
|
||||
"url": "https://yoururl.com",
|
||||
"description": "Description",
|
||||
}
|
||||
}
|
||||
`} </script>
|
||||
|
||||
</Helmet>
|
||||
//...
|
||||
```
|
||||
|
||||
|
||||
These resources provide the information needed to get the most out of structured data:
|
||||
- [Introduction to structured data markup](https://developers.google.com/search/docs/appearance/structured-data/intro-structured-data)
|
||||
- [General structured data guidelines](https://developers.google.com/search/docs/appearance/structured-data/sd-policies)
|
||||
|
||||
After you have a small notion about them, you can go deeper by adding custom functions depending on your app (FAQs, Rating, Review, Software Application...):
|
||||
- [ALL structured data functions](https://developers.google.com/search/docs/appearance/structured-data/search-gallery)
|
||||
|
||||
|
||||
:::tip[Star our Repo on GitHub! 🌟]
|
||||
We've packed in a ton of features and love into this SaaS starter, and offer it all to you for free!
|
||||
|
6
template/app/public/robots.txt
Normal file
6
template/app/public/robots.txt
Normal file
@ -0,0 +1,6 @@
|
||||
User-agent: *
|
||||
Allow: /
|
||||
|
||||
Disallow: /admin/
|
||||
Disallow: /api/
|
||||
Disallow: /auth/
|
@ -22,7 +22,11 @@ export default function Testimonials({ testimonials }: { testimonials: Testimoni
|
||||
</blockquote>
|
||||
<figcaption className='mt-6 text-base text-white'>
|
||||
<a href={testimonial.socialUrl} className='flex items-center gap-x-2'>
|
||||
<img src={testimonial.avatarSrc} loading='lazy' className='h-12 w-12 rounded-full' />
|
||||
<img
|
||||
src={testimonial.avatarSrc}
|
||||
alt={`Profile picture of ${testimonial.name}`}
|
||||
className='h-12 w-12 rounded-full'
|
||||
/>
|
||||
<div>
|
||||
<div className='font-semibold hover:underline'>{testimonial.name}</div>
|
||||
<div className='mt-1'>{testimonial.role}</div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user