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

Set Metadata on component level #2153

Open
samanthaming opened this issue Jan 26, 2020 · 18 comments
Open

Set Metadata on component level #2153

samanthaming opened this issue Jan 26, 2020 · 18 comments
Assignees
Labels
status: core team review Priority issues for the core team to review type: feature request Request to add a new feature

Comments

@samanthaming
Copy link

Feature request

Currently the way to set custom metadata is through the frontmatter in the markdown file. It'd be very helpful if we could set this on the component level.

The Problem

Here's a scenario of why this is important. I use Cloudinary to store my images and I use a gulp task to upload the images. Which then populates a single javascript object with all of the ID of the images. These are the images I want to use in my meta head (especially for my social media links on twitter and facebook). And I really don't want to set them manually in each of my markdown frontmatter.

Ideal Solution

It'd be so nice, if I could set the meta head directly in my layout or component. Something like vue-meta from Nuxt.

Alternative Solution

Unfortunately, I had trouble trying to figure out how to set the vue-meta plugin to work with VuePress via the SSR way. So instead I created a custom plugin that will programmatically set the frontmatter with frontmatter.meta. It's working in my dev environment, however, when the site is built, it then has duplicates (I just found this out and have not looked into it yet, when I find out why, I'll update this issue).

Conclusion

I know VuePress is mainly built for technical documentation. But for those using it for more than that, I think meta data is a super important feature. I used VuePress to build my blog and I picked a static site generator for SEO reason. And meta data is an integral part of that.

Overall I loved using VuePress to build my new site (the markdown support and the built-in header search are amazing!). All that's missing is this meta data support. Not trying to compare VuePress to others, but Gridsome offers this. And it's probably one of the feature that might make me want to switch over.

Anyways, I think the team did a fabulous job on this. I hope you will consider this feature! 👏

@billyyyyy3320 billyyyyy3320 added the type: feature request Request to add a new feature label Jan 27, 2020
@bencodezen
Copy link
Member

bencodezen commented Jan 29, 2020

Thanks for filing this @samanthaming! We really appreciate the time and effort you took to investigate this and will add this our list of items to discuss for our roadmap! Would you be interested in code pairing sometime so I can get a better understanding of the problem and your current solution?

@bencodezen bencodezen added priority: medium Medium priority issue priority: high High priority issue status: core team review Priority issues for the core team to review and removed priority: medium Medium priority issue priority: high High priority issue labels Jan 29, 2020
@samanthaming
Copy link
Author

Thanks @bencodezen for looking into this 👏 Absolutely! I'll reach out to you and we can set something up 🙌

@haoranpb haoranpb mentioned this issue Feb 2, 2020
18 tasks
@adamdehaven
Copy link
Contributor

adamdehaven commented Jul 27, 2020

To follow up on @samanthaming's comment, I also wrote a custom plugin that injects metadata into the ssrTemplate - would be glad to share, if helpful.

@samanthaming
Copy link
Author

@adamdehaven wooo you have a custom plugin 🤩 is it an open source that you can provide a link to? Would love to check it out 😊

@adamdehaven
Copy link
Contributor

adamdehaven commented Jul 28, 2020

@samanthaming I haven't released it yet, but plan to (still doing a little testing). I just redesigned my site with VuePress as well and am getting ready to push the site itself live in the coming days.

The plugin I wrote injects the canonical URL, Schema.org structured data, as well as extra metadata into the ssrTemplate during the build process (so the tags will not be present when running npm run docs:dev)

@adamdehaven
Copy link
Contributor

adamdehaven commented Aug 5, 2020

@bencodezen I may (huge question mark here) have found a somewhat easy way to implement part of the ask here (canonical URL tag), which could likely be expanded to allow for dynamic injection.

If the check for headerType was updated in vuepress/packages/@vuepress/core/lib/client/root-mixins/updateMeta.js as shown below, I believe the canonical URL could be injected along with the rest of the metadata in the frontmatter.

// vuepress/packages/@vuepress/core/lib/client/root-mixins/updateMeta.js

export default {
  // created will be called on both client and ssr
  created () {
    this.siteMeta = this.$site.headTags
      // .filter(([headerType]) => headerType === 'meta') // OLD
      .filter(([headerType]) => ['meta', 'link'].includes(headerType)) // NEW: allows for 'meta' and 'link' tags 
      .map(([_, headerValue]) => headerValue)

      // ... more code

Then, in a page's frontmatter:

---

# Defining page-level metadata - unchanged (goes back to original request to allow for dynamic metadata)
meta: 
    - name: description
      content: Page description text

# Defining other link data (although the requested link in Issues is currently canonical URL)
link:
    - href: https://www.example.com/canonical/url/link/
      rel: canonical

---

This still requires the values be hard-coded into the frontmatter; however, is an easy way to expand the type of tag that can be injected via frontmatter.

The problem with other canonical URL solutions (as well as injecting metadata) is that they only load for the current page and then are not updated dynamically (as in the build process, not client-side) as the pages change. The correct URLs display in the /dist/ directory pages after build; however, the VuePress SPA never refreshes the attributes when moving from page to page. As an example, the code below probably looks familiar:

created() {
    if (typeof this.$ssrContext !== "undefined") {
        this.$ssrContext.userHeadTags += `\n    <link rel="canonical" href="${this.computeURL()}"/>`;
    }
}

This only loads the proper URL (or other content) for the first-requested page. Any subsequent navigation within the VuePress SPA does not update the properties, even if you watch $page or any of the other hacks that are out there.

@bencodezen
Copy link
Member

@adamdehaven Great research! Would you be interested in opening up a PR on this?

In the long run, we probably need to find a way to integrate vue-meta as its API is fairly ubiquitous in the Vue community these days. Hoping to get to this at some point, but your solution seems to be a great way to open up possibilities in the meantime. Let me know what you think!

@adamdehaven
Copy link
Contributor

adamdehaven commented Aug 12, 2020

@bencodezen integrating vue-meta would be great, but I'm not familiar what all it allows you to do... I'll have to take a look.

I should be able to submit a PR; however, I'm traveling and it may be next week.

Question: would implementing the way I suggested above also open up the properties to the extendPageData method in the Option API? If yes, then I'll probably create the PR to also include script tags, which will also allow for injecting Schema.org structured data into the page as well! 🎉

If I allow script tags as well, and wanted to include JSON as the content (instead of adding an attribute) how would this be structured in YAML?

@bencodezen
Copy link
Member

@adamdehaven No worries as far as urgency. In the event I manage to get a head start, I'll be sure to update the thread so you're aware 👍

In regards to your question, I inherited the project after its release, so I'm still learning the ins and outs of the codebase. If you discover the answer to this before I do, would love to get your findings documented so other people have an easier time contributing in the future!

@d-pollard
Copy link
Collaborator

Started my cursory overview of this and had a few quick questions here:

Would we want to implement vue-meta here first, or come up with a stopgap?

If moving to vue-meta is the answer, do we try and incorporate its API into our existing frontmatter, or do we push to use vue-meta and introduce breaking changes?

If the answer isn't vue-meta, then would we try to implement our own API? I'm not too keen on this one as it seems to reinvent the wheel with the added bonus maintenance for the VuePress team

Any and all feedback/dialogue is welcome!

@adamdehaven
Copy link
Contributor

@d-pollard I think implementing vue-meta may be the way to go; however, the big piece here is to allow that data to be available in the DOM immediately (as is the case with current meta info) instead of it having to render along with the rest of the Vue components. I have a custom plugin I wrote (similar to @samanthaming) where I dynamically inject the metadata; however, the vue-meta implementation wouldn't account for canonical URLs, structured data, or as I understand how it would work with VuePress, allowing for the tags to be available for SEO purposes (e.g. site scrapers, robots, etc.)

@d-pollard
Copy link
Collaborator

@adamdehaven - thanks for the feedback

From what I've read on vue-meta, it seems like they come with SSR support so I believe it'd be available straight away in the DOM

I'm concerned, however, with how we'd introduce vue-meta then, since we already have meta information coming from frontmatter, would we try to build on top of that API with vue-meta (something I'm not sure is possible) or would we move the meta information away from frontmatter and try to use a special component that binds to vue-meta?

@adamdehaven
Copy link
Contributor

adamdehaven commented Oct 12, 2020

@d-pollard so just thinking out loud, but here goes:

What if VuePress included vue-meta available to turn on/off via a setting in the .vuepress/config.js?

  • If turned off, then metadata is configured/set up as currently defined in the VuePress docs, and is still extendable through the extendPageData method, again, as is currently supported via the VuePress Options API.
  • If vue-meta integration is turned on, then you could go a couple routes:
    1. If the vue-meta metaInfo object IS defined by the user, it would take precedence. Any meta attributes could be defined on the app level, or individual page level, with the more-specific (e.g. page) level taking precedence.
    2. If the vue-meta metaInfo object is NOT defined by the user, then it could still be automatically used by VuePress under the hood, but instead of looking for the metaInfo object provided by the user, the values could be mapped to the the classic frontmatter meta properties, if they are set (some or all) on the app/page level. This should happen after all VuePress Option API methods are complete (e.g. if the user is utilizing the extendPageData method) to allow the user to still be able to dynamically customize values, etc.

If VuePress does do something to this effect, I strongly plead for a way to also include a page's canonical URL in the frontmatter so that it can be injected directly into each page during SSR so it's available in the DOM (and, this would hopefully be compatible if using the VuePress Blog Plugin as well). With AMP pages, article syndication (e.g. Medium, Dev.to, etc.) the canonical URL tag is super-important. I realize this would likely be a separate feature/PR, I believe it could likely be integrated in a similar fashion to what is trying to be solved for here 🙌

Edit: spelling is hard.

@d-pollard
Copy link
Collaborator

@adamdehaven - since the canonical bit was a quick win here, I've opened this PR: #2658

Mind reviewing it and giving your thoughts for me?

@adamdehaven
Copy link
Contributor

adamdehaven commented Oct 13, 2020

@d-pollard from what I've tested so far, PR #2658 does not work. The canonical link tag is added to the first page that contains the corresponding frontmatter entry; however, the value of the link tag does not update when the route changes.

For example, using the official @vuepress/plugin-blog on my site, when I add the changes in the PR, the canonical URL is correct on the first page /about/ but does not update on route change to /blog/post-slug/.

(Same behavior occurs without using the blog plugin as well)

Update: Moving comments to PR #2658

@d-pollard
Copy link
Collaborator

@adamdehaven - my apologies, I forgot to commit my most recent change; please try again with the newest changes

@adamdehaven
Copy link
Contributor

@d-pollard moving the PR conversation to #2658 thread 😄

@d-pollard
Copy link
Collaborator

@adamdehaven @samanthaming

This PR introduces Vue Meta: #2661

it is a backwards compatible replacement for current setup, please add any questions or concerns in that thread

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
status: core team review Priority issues for the core team to review type: feature request Request to add a new feature
Projects
None yet
Development

No branches or pull requests

5 participants