Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds new <Metadata> component for proper <meta> tag generation #9315

Merged
merged 30 commits into from
Dec 19, 2023
Merged

Conversation

cannikin
Copy link
Member

@cannikin cannikin commented Oct 18, 2023

Rather than trying to only allow certain props and try to transform one to another, we're going to allow you to basically enter anything you want, but with a couple of conventions.

I propose keeping the existing <MetaTags> functionality as it exists and just add <Metadata> as a whole new concept. This allows us to release sooner than 7.0, since it would be non-breaking, and makes it easier for people to upgrade over time rather than all at once: having to change potentially hundreds of existing <MetaTags> declarations (across pages and cells) before they can deploy their app again would not be fun. I'll make it clear in the docs that they really want to move to <Metadata> once SSR is available in order to get maximum benefit for bots crawling their site.

Definitions

  • Top level prop means a plain text name like "title" or "description"
  • Namespaced prop means a name with one or more colons, like og:image

Rules

Any "top level" prop will create a <meta> tag with name and content attributes

<Metadata description="Lorem ipsum dolar sit amet..." />
// generates
<meta name="description" content="Lorem ipsum dolar sit amet..." />

You can add child elements which will be appended to any you create as props

<Metadata description="Lorem ipsum dolar sit amet...">
  <meta httpEquiv="refresh" content="30" />
</Metadata>
// generates
<meta name="description" content="Lorem ipsum dolar sit amet..." />
<meta http-equiv="refresh" content="30" />

Any "namespaced" props will create a <meta> tag with property and content attributes

<Metadata og={{ image:"http://host.test/image.jpg" />
// generates
<meta property="og:image" content="http://host.test/image.jpg" />

You can create multiple <meta> tags with the same name/property, the OpenGraph spec encourages this

<Metadata og={{ image: ["http://host.test/image1.jpg", "http://host.test/image2.jpg"] />
// generates
<meta property="og:image" content="http://host.test/image1.jpg" />
<meta property="og:image" content="http://host.test/image2.jpg" />

You can combine values and child namespaces

<Metadata
  og={{
    image: [
      'http://host.test/image1.jpg',
      { width: 300, height: 300 },
      'http://host.test/image2.jpg',
      'http://host.test/image3.jpg',
      { height: 1000 },
    ],
  }}
/>
// generates
<meta property="og:image" content="http://host.test/image1.jpg" />
<meta property="og:image:width" content="300" />
<meta property="og:image:height" content="300" />
<meta property="og:image" content="http://host.test/image2.jpg" />
<meta property="og:image" content="http://host.test/image3.jpg" />
<meta property="og:image:height" content="1000" />

If you define any og prop, we will copy any title and description to an og:title and og:description tag.

<Metadata title="My Website" og />
// generates
<meta name="title" content="My Website" />
<meta property="og:title" content="My Website" />

You can override this behavior by explicitly setting og:title to null:

<Metadata title="My Website" og={{ title: null }}/>

If you define any og prop, we will create an og:type set to website:

<Metadata og />
// generates
<meta property="og:type" content="website" />

You can override this behavior by explicitly setting og:type:

<Metadata og={{ type: 'music:album' }}/>

If you define a charSet prop will we create the special <meta charSet> tag:

<Metadata charSet="utf-8" />
// generates
<meta charset="utf-8" />

If you define a locale prop we will inject the locale attribute into the opening <html> tag:

<Metadata locale="en-US" />
// generates
<html locale="en-US">
  <!-- ... -->
  <meta name="locale" content="en-US" />

This should allow the <meta> generation to be as flexible as possible, allowing you to create twitter, facebook, and any other OpenGraph-like properties in the future:

<Metadata
  title="My Website"
  description="Lorem ipsum dolar sit amet..."
  charSet="utf-8"
  locale="en-US"
  og={{
    image: [
      'https://example.com/rock1.jpg',
      { width: 300, height: 300 },
      'https://example.com/rock2.jpg', 
      { width: 600, height: 600 },
    ],
  }}
  twitter={{
    card: 'summary',
    site: '@spoke',
    creator: '@cannikin',
  }}
  fb={{
    app_id: '1234567890',
  }}
/>

Note for releasing

There’s a comment in the doc about “Prior to v7.0 Redwood provided a <MetaTags> helper that has now been deprecated…” so if this goes in earlier than 7 we’ll want to update that text.

@cannikin cannikin added topic/web release:breaking This PR is a breaking change labels Oct 18, 2023
@cannikin cannikin added this to the v7.0.0 milestone Oct 18, 2023
@dac09
Copy link
Contributor

dac09 commented Oct 19, 2023

So the idea with MetaTags, was to have a component that simplifies generating all these headers. Like you supply one prop, and we render 5. e.g. title would generate not just the <title> but the meta title, twitter title, etc.

Since this is named it seems like we shouldn't be adding/modifying non-meta tags, but there are a couple of exceptions:

Although title isn't technically a meta tag, it certainly is treated like one which is why it was here - I think it still should be there. Perhaps we should rename to SeoTags instead - because that's the real purpose here.

If you wanted more custom control, or the metatags wasn't letting you do something, I would say its just better to use <Head/> so you don't need to learn yet-another-syntax for the same thing.

@cannikin cannikin changed the title Update <MetaTags> for proper OpenGraph generation and consistency Update <MetaTags> (now <Metadata>) for proper OpenGraph generation and consistency Oct 25, 2023
@cannikin cannikin added release:feature This PR introduces a new feature and removed release:breaking This PR is a breaking change labels Oct 25, 2023
@cannikin cannikin changed the title Update <MetaTags> (now <Metadata>) for proper OpenGraph generation and consistency Adds new <Metadata> component for proper <meta> tag generation Oct 25, 2023
@cannikin cannikin marked this pull request as ready for review October 30, 2023 20:58
@cannikin cannikin requested a review from dac09 October 30, 2023 20:58
@cannikin
Copy link
Member Author

cannikin commented Nov 1, 2023

Docs have been updated! I wasn't sure what version this was going to release, and I mentioned "As of Redwood 7.0..." so if it looks like we're going to release sooner than that, let me know and I'll update the version mentioned.

@cannikin
Copy link
Member Author

cannikin commented Nov 1, 2023

Added a deprecation notice to <MetaTags> in JSDoc as suggested by @Tobbe

@dac09
Copy link
Contributor

dac09 commented Nov 8, 2023

I propose keeping the existing functionality as it exists and just add as a whole new concept.

while I like this idea, Tom suggested when we discussed this that we should just make it a breaking, instead of deprecating - so as to not confuse people with these very similarly named components!

@cannikin
Copy link
Member Author

cannikin commented Nov 8, 2023

while I like this idea, Tom suggested when we discussed this that we should just make it a breaking, instead of deprecating - so as to not confuse people with these very similarly named components!

He did, but I showed him this solution and he loved it! I believe he was against keeping the name <MetaTags> and then continuing to support the previous props like ogImageUrl in addition to the new props with the nested stuff. Gross!

@Tobbe Tobbe modified the milestones: SSR, v7.0.0 Dec 19, 2023
@jtoar jtoar modified the milestones: v7.0.0, next-release Dec 19, 2023
@jtoar jtoar merged commit 8ecec88 into main Dec 19, 2023
33 checks passed
@jtoar jtoar deleted the rc-meta-tags branch December 19, 2023 19:39
Tobbe added a commit that referenced this pull request Dec 21, 2023
Rather than trying to only allow certain props and try to transform one
to another, we're going to allow you to basically enter anything you
want, but with a couple of conventions.

I propose keeping the existing `<MetaTags>` functionality as it exists
and just add `<Metadata>` as a whole new concept. This allows us to
release sooner than 7.0, since it would be non-breaking, and makes it
easier for people to upgrade over time rather than all at once: having
to change potentially hundreds of existing `<MetaTags>` declarations
(across pages and cells) before they can deploy their app again would
not be fun. I'll make it clear in the docs that they really want to move
to `<Metadata>` once SSR is available in order to get maximum benefit
for bots crawling their site.

### Definitions

* **Top level prop** means a plain text name like "title" or
"description"
* **Namespaced prop** means a name with one or more colons, like
`og:image`

### Rules

#### Any "top level" prop will create a `<meta>` tag with `name` and
`content` attributes

```jsx
<Metadata description="Lorem ipsum dolar sit amet..." />
// generates
<meta name="description" content="Lorem ipsum dolar sit amet..." />
```

#### You can add child elements which will be appended to any you create
as props

```jsx
<Metadata description="Lorem ipsum dolar sit amet...">
  <meta httpEquiv="refresh" content="30" />
</Metadata>
// generates
<meta name="description" content="Lorem ipsum dolar sit amet..." />
<meta http-equiv="refresh" content="30" />
```

#### Any "namespaced" props will create a `<meta>` tag with `property`
and `content` attributes

```jsx
<Metadata og={{ image:"http://host.test/image.jpg" />
// generates
<meta property="og:image" content="http://host.test/image.jpg" />
```

#### You can create multiple `<meta>` tags with the same name/property,
the OpenGraph spec encourages this

```jsx
<Metadata og={{ image: ["http://host.test/image1.jpg", "http://host.test/image2.jpg"] />
// generates
<meta property="og:image" content="http://host.test/image1.jpg" />
<meta property="og:image" content="http://host.test/image2.jpg" />
```

#### You can combine values and child namespaces

```jsx
<Metadata
  og={{
    image: [
      'http://host.test/image1.jpg',
      { width: 300, height: 300 },
      'http://host.test/image2.jpg',
      'http://host.test/image3.jpg',
      { height: 1000 },
    ],
  }}
/>
// generates
<meta property="og:image" content="http://host.test/image1.jpg" />
<meta property="og:image:width" content="300" />
<meta property="og:image:height" content="300" />
<meta property="og:image" content="http://host.test/image2.jpg" />
<meta property="og:image" content="http://host.test/image3.jpg" />
<meta property="og:image:height" content="1000" />
```

#### If you define *any* `og` prop, we will copy any `title` and
`description` to an `og:title` and `og:description` tag.

```jsx
<Metadata title="My Website" og />
// generates
<meta name="title" content="My Website" />
<meta property="og:title" content="My Website" />
```

You can override this behavior by explicitly setting `og:title` to
`null`:

```jsx
<Metadata title="My Website" og={{ title: null }}/>
```

#### If you define *any* `og` prop, we will create an `og:type` set to
`website`:

```jsx
<Metadata og />
// generates
<meta property="og:type" content="website" />
```

You can override this behavior by explicitly setting `og:type`:

```jsx
<Metadata og={{ type: 'music:album' }}/>
```

#### If you define a `charSet` prop will we create the special `<meta
charSet>` tag:

```jsx
<Metadata charSet="utf-8" />
// generates
<meta charset="utf-8" />
```

#### If you define a `locale` prop we will inject the `locale` attribute
into the opening `<html>` tag:

```jsx
<Metadata locale="en-US" />
// generates
<html locale="en-US">
  <!-- ... -->
  <meta name="locale" content="en-US" />
```

---

This should allow the `<meta>` generation to be as flexible as possible,
allowing you to create twitter, facebook, and any other OpenGraph-like
properties in the future:

```jsx
<Metadata
  title="My Website"
  description="Lorem ipsum dolar sit amet..."
  charSet="utf-8"
  locale="en-US"
  og={{
    image: [
      'https://example.com/rock1.jpg',
      { width: 300, height: 300 },
      'https://example.com/rock2.jpg', 
      { width: 600, height: 600 },
    ],
  }}
  twitter={{
    card: 'summary',
    site: '@spoke',
    creator: '@cannikin',
  }}
  fb={{
    app_id: '1234567890',
  }}
/>
```

## Note for releasing

There’s a comment in the doc about “Prior to v7.0 Redwood provided a
`<MetaTags>` helper that has now been deprecated…” so if this goes in
earlier than 7 we’ll want to update that text.

---------

Co-authored-by: Daniel Choudhury <dannychoudhury@gmail.com>
Co-authored-by: Tobbe Lundberg <tobbe@tlundberg.com>
@Tobbe Tobbe modified the milestones: next-release, v6.6.0 Dec 22, 2023
@gcallsen
Copy link

Thank you, all! Minor typo in Docs (https://redwoodjs.com/docs/canary/seo-head#setting-meta-tags-and-opengraph-directives-with-metadata) --> The example code shows import { MetaTags } from '@redwoodjs/web' instead of intended import { Metadata } from '@redwoodjs/web'

@Tobbe
Copy link
Member

Tobbe commented Dec 22, 2023

@gcallsen Thanks for reporting! PR here: #9744

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
release:feature This PR introduces a new feature topic/web
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants