docusaurus icon indicating copy to clipboard operation
docusaurus copied to clipboard

RFC: social cards

Open slorber opened this issue 4 years ago β€’ 55 comments

Issue for idea already expressed here: https://github.com/facebook/docusaurus/issues/2267#issuecomment-639597335

Dynamic Social Cards

You might have noticed that some people / companies have an automated way to generate social cards that adapt to the underlying content (generating an image from query string params)

I think this would cool if by default, all Docusaurus docs/blogs would generate custom social cards, if the user did not define any custom social image.

This way, all Docusaurus links pasted to a social media would have a beautiful image.

By default we could even include a discrete Docusaurus + Slash logo for growth hacking πŸ€ͺ

Vercel has a nice project to do this easily: https://github.com/vercel/og-image

We could fork this project, host our own variant (can be free on Vercel), and use it for D2 website, + allow other sites to use it as well.

Curretly, Docusaurus can have a default social card, but it's static and does not adapt to the content::

image

Here's some examples of dynamic/automated social cards:

image

image

image

image

slorber avatar Jun 19 '20 09:06 slorber

Notes, there are many ways to handle these social cards.

  • We could generate the cards as a build step, using a headless chromium instance in the CIs that support it.
  • We could host lambdas to generate the images remotely
  • We could use saas services like Cloudinary...

Interesting blog post: https://www.swyx.io/writing/jamstack-og-images/


@JoelMarcey

I think this is a good idea and I would be interesting in having this implemented. As far as how to implement it, I have no strong feelings. I am generally in favor of utilizing other solutions for specific features if we believe that the cost of implementing it ourselves would be so much greater. That said, we lose a little bit of control of our destiny by doing that. I am definitely curious which way others think we should go here.

What I think about potential implementations:

Generating the images at build time

Would require some kind of headless browser in the build process. I think it's likely to complicate the build process, make it slower, less portable, and potentially exceeding free quotas of popular free CIs. I think it's not the best solution.

Seen this being done here with Gasby: https://andrewingram.net/posts/automatic-social-cards-with-gatsby/

Using a SaaS

Some people are using a SaaS like Cloudinary to generate these cards, so they don't have to host any infrastructure.

For example: https://www.learnwithjason.dev/blog/auto-generate-social-image/

Self hosted

We could create our own solution and host it.

Other solutions are possible, but I mean forking https://github.com/vercel/og-image as a docusaurus-images project, and hosting it on Vercel for free (or negotiate some kind of free open-source usage with them).

We would make the service flexible enough so that it works great for most of the sites, yet enabling users to self host an alternative service if our default service does not suit their needs.


What I think we should do:

  • Create a docusaurus-images (can be a fork of vercel/og-image)
  • Make it flexible enough for our need (querystring should enable to customize the social card image, enable/disable a Docusaurus stamp, show docs version...)
  • Host it on Vercel, making it available by default to all without having to do anything
  • Expose a configuration function that takes params of the page, and return the og image url, (which by default would use the service we host)

This way, we don't complicate the build by default, yet we enable users to craft their own social image urls. They could self-host their own fork of vercel/og-images, use external services like Cloudinary, or even add the image generation as a build step using a docusaurus postBuild plugin.

slorber avatar Jun 19 '20 10:06 slorber

hi! me and @Drewbi will be working on this issue :)

FocalChord avatar Jun 22 '20 03:06 FocalChord

Great!

@anshulrgoyal also suggested we could simply use an image processing lib like sharp to create the card at build time. That's simpler than using a headless chrome, and that's worth creating a POC to see how far we can go with this approach.

slorber avatar Jun 22 '20 09:06 slorber

I was suggesting creating some template for cards and then allowing the user to provide custom stuff like text, icons, logo, images cards and we can have templates that can read mark down and extract stuff from it like for blog we can extract image and title, author and create image from it.

anshulrgoyal avatar Jun 22 '20 10:06 anshulrgoyal

Yes, in the end we should be able to add a meta og image tag in generated pages + an image in the build folder. We also need to think about where we plug this image file generation step, we don't have anything like that in D2 afaik. Also need to think about how to make it generic so that it can work for docs, blog, pages... what's the url of such image etc... Not so simple :)

slorber avatar Jun 22 '20 10:06 slorber

I'd personally prefer customizing the existing og-image project as many of the features are very relevant to what we are trying to achieve and all that would be required in terms of modification would be altering the html/css that gets generated internally by the function.

In order to integrate the process locally at build time we would have to create and store an image for every page and either keep track of updates or regenerate the images on the next build. While possible, at this point I feel the API would lend itself quite nicely to the job and would allow for a light weight and easy implementation. It would also allow for users to swap out endpoints if they want like @slorber mentioned.

The customization of fields would be as easy as including or leaving out fields from the query string, eg. og-docusaurus.now.sh/main%20text?logo=""&author=""&sitetitle=""&docv="2.13.4" Any of these fields could be left out and it could be handled by the API.

Either way, if we agree on some desirable fields, I can start creating designs/mockups 🎨

Drewbi avatar Jun 23 '20 05:06 Drewbi

Some random thoughts.

I think it should be integrated at the theme classic level. Other themes would be responsible for adding themselves the og image system they want (if they do).

I think the theme should allow a function to generate the og image url for each page. It would take some data we provide as parameter

const config { 
  themeConfig: {
    createOGImage: ({data,defaultImage}) => {
      if (data.type === "myCustomDocType" {
        return "https://cloudinary.com/someThing?"+serialize({title: data: title});
      }
      return defaultImage; // defaultImage would be an url to the hosted og-docusaurus hosted service 
    }
  }
}

Eventually, we could allow the user to generate himself the images at build time using an async function?

const config { 
  themeConfig: {
    createOGImage: async (data) => {
        const relativeFilePath = path.join("og_images",random()+".jpg");
        const filePath = path.join(__dirname,"build","og_images",random()+".jpg");
        await writeFile(filePathgenerateOgImageWithSharp(data));
        return "https://myDocusaurusSite/"+baseUrl + relativeFilePath;
    }
  }
}

This function couldn't be called from React directly (as the browser does not have access to nodejs apis like sharp/fs etc), so I think the blog/docs plugin should call it with the blog/doc metadata, and inject it into the theme components as props. (BlogPostPage/DocItem)


Not totally sure about this api, feedbacks welcome. We should probably make the system generic enough so that it can be used as well for custom pages (+ the upcoming markdown pages: https://github.com/facebook/docusaurus/pull/2947).


@Drewbi I think it's safe to say that we need at least 3 types of social cards:

  • docs: title, version?...
  • blog: title, author, author picture, tags...
  • pages: title

I think the fields should vary according to the kind of docs, so we probably need multiple designs.

In all cases I think including the site logo is nice, and an opt-out Docusaurus logo for growth hacking πŸ€ͺ

slorber avatar Jun 23 '20 15:06 slorber

@slorber I was thinking of adding it to some place like StaticSiteGenerator plugin. We can allow the user to modify options and pass a custom function that will be envoked inside ejs, with all the page meta data

anshulrgoyal avatar Jun 23 '20 16:06 anshulrgoyal

Some site bots don't render javascript

anshulrgoyal avatar Jun 23 '20 16:06 anshulrgoyal

@slorber I was thinking of adding it to some place like StaticSiteGenerator plugin. We can allow the user to modify options and pass a custom function that will be envoked inside ejs, with all the page meta data

That could be nice to see a poc of this, even if there are lots of things hardcoded in the PR, to see if it can fit the usecases. But this means we would perhaps need additional metadata to be able to inject back all the required data to the fn (for example, the type of doc "blog"/"doc"."page"/other...)

I think it's ok to use custom meta elements anyway so it shouldn't be a big deal, + we could make a Helmet wrapper to help the user provide us the data needed for custom pages.

Some site bots don't render javascript

I don't understand πŸ˜…

slorber avatar Jun 24 '20 15:06 slorber

Made some initial experiments, am still not completely satisfied with any so will continue experimenting but figure I'd put them out there if anyone has suggestions for things to try or comments about which ones work best.

Mockups

For a closer look: https://xd.adobe.com/view/5f228e2f-3db9-4755-91be-2b6f95e04de2-0a71/

Drewbi avatar Jul 02 '20 11:07 Drewbi

That looks like a nice start, great.

For versioned docs, I'd say we should make the version smaller and closer to the bottom. And for the Docusaurus icon, I think many people don't know it so it's rather write something like " - published with Docusaurus" in very small. But would have to see it to be sure :p

slorber avatar Jul 03 '20 08:07 slorber

@slorber I'd like to take this as an issue for MLH Fellowship now. CC: @lex111

sarthakkundra avatar Nov 25 '20 18:11 sarthakkundra

Yes @sarthakkundra thanks πŸ‘

Do you have good design skills to make some proposals for the social cards? We should rather have a different card for each kind of content type, yet all the cards should be consistent with each others imho.

Also worth looking at existing PR draft code from former MLH (just for inspiration, does not mean we have to keep that existing code)

slorber avatar Nov 26 '20 16:11 slorber

@slorber I have terrible design skills :/ but there is an Adobe XD wireframe link in this thread which I can use? I think that'll go with OG-Vercel too as we can change Vercel's logo with D2's and probably play around with the font.

I had a look at the PR. Will take inspiration from it once I've setup the Vercel API to return Images / SVGs based on our query string.

Let me know if you have an implementation in mind or want to add something to this approach.

sarthakkundra avatar Nov 26 '20 17:11 sarthakkundra

I don't think you need to master a design tool, as long as you can make a pretty html/css card that's already nice

I don't have much more API ideas that what is written here (it's been a long time I didn't think about this problem). The best is probably to give a try based on my former comments and propose an implementation, and we'll try to find its shortcomings once we have a POC?

slorber avatar Nov 26 '20 17:11 slorber

Sounds good!

sarthakkundra avatar Nov 26 '20 17:11 sarthakkundra

@sarthakkundra I also built a POC but it explored an alternate approach, it might worth looking into. https://github.com/MLH-Fellowship/docusaurus/tree/anshul/ogimage

anshulrgoyal avatar Nov 26 '20 17:11 anshulrgoyal

@slorber Made some mockups. Let me know the feedback, will modify og-Vercel accordingly to create our Image as a service provider which we can later host. Social Cards Mockup

Check out this codepen for background options

sarthakkundra avatar Nov 28 '20 10:11 sarthakkundra

Thanks, that looks nice. We probably want more padding at the top.

slorber avatar Nov 30 '20 13:11 slorber

@slorber I have hosted the OG image service. Here's a preview. Let me know your thoughts. Also if required I can write a small documentation for the URL query parameters.

sarthakkundra avatar Dec 02 '20 11:12 sarthakkundra

yes that looks nice @sarthakkundra

That would be helpful to also work on the UI/form here https://og-image-delta-six.vercel.app/ This way users will more easily be able to play with all the possible variations.

It's probably better to make the text slightly larger At the same time we should handle nicely docs/blogs with both small/long titles. We can let the user customize the size, and by default choose a good size according to the length of the content? (We can probably truncate and limit to 2 lines)

slorber avatar Dec 03 '20 10:12 slorber

@slorber Yes. This was just a POC will make the UI match with our preferences.

sarthakkundra avatar Dec 03 '20 10:12 sarthakkundra

Problem:

  • themeConfig.socialCardFn is complicated to achieve, as configFn can't be executed on client-side theme code

What I suggest for now is:

  • Have themeConfig.socialCardService.url, default to https://og-image-delta-six.vercel.app/
  • Have themeConfig.socialCardService.defaultParams: the default params added to the social card service querystring
  • Create a @theme/SocialCard component, with some meaningful props to construct the appropriate image url with querystring and put it in the page with <Head>
  • Use SocialCard in DocPage and BlogPage with relevant props

It's not perfect but it lets:

  • User provide his own social card service url that he could customize by forking the default one
  • Swizzle @theme/SocialCard if he wants to have full control over querystring params and final url construction

slorber avatar Dec 03 '20 11:12 slorber

This sounds very exciting, may I ask what the progress with this is?

yiliansource avatar Jun 23 '21 12:06 yiliansource

No progress has been made on this

slorber avatar Jun 24 '21 12:06 slorber

I see. I really like the design mockups by @Drewbi, they're a great start. What I struggle to understand is the following:

The latest stance seems to be to have a service running to generate the social cards dynamically, without having to build them. But doesn't this reliability on an "external" service and the constant generation on their end, whenever the preview should be displayed, become sort of a liability?

From what I can see, there were already plans to do this during build time, using some kind of image processing library. This could definitely be restricted to production builds, to avoid development builds with docusaurus start taking ages - social card previews aren't of much use there anyway. Performing the build locally provides the luxury of having the static assets in place reliably and loading them fast. Styling could be done via basic, templated HTML (similar to swizzled components?), which is rendered to JPGs during the static build. Of course, we could even make this toggle-able between static generation (local URLs) and on-the-fly service generation (external URLs).

@slorber Is this issue up for grabs? Could be fun to give this a shot, if we're on the same page with the ideas!

yiliansource avatar Jun 24 '21 15:06 yiliansource

@yiliansource nobody is working on it currently, and I won't either, so if you want to make a POC you can.

I'd like this to be enabled by default for all sites. And some sites have a lot of docs, so if we build the snapshots locally, we'd rather have a good caching layer for generated images or it will be time-consuming for large sites by default.

A remote service to render images is a SPOF that we have to maintain, but I think we could provide that to the community: it wouldn't be too complicated to run on Netlify lambdas with an immutable caching strategy.

We should probably offer to generate assets both locally (at build time + caching) and lazily/remotely (with a lambda service). We can think about the best default behavior later.

slorber avatar Jun 24 '21 17:06 slorber

@slorber i'll see if I can craft something up. I'll be honest, I haven't really been using Netlify/Vercel at all yet, but better starting late with it than never.

The argument of quantity is fair - I guess we'll go for the lambda service first then. I'll try to stir something up similar to https://github.com/vercel/og-image, deployed with Vercel so we can test it. Once we're happy with the generator we can look into integrating into Docusaurus.

I don't assume there's a standard or desired URL/query pattern that the service should provide?

yiliansource avatar Jun 24 '21 17:06 yiliansource

I don't assume there's a standard or desired URL/query pattern that the service should provide?

We have to design the url/query API that the service will provide, and also pass all the meaningful params in a custom async callback provided by the user in case he wants to use another service for generating images.

For now having a POC of this is already nice, we can think later about what exactly the api/params should be, but I can think of title, page type, icon...

slorber avatar Jun 24 '21 18:06 slorber

Quick update: I managed to patch together an image generation service at https://github.com/yiliansource/docusaurus-og-image, however there are some issues with my current setup when deployed to Vercel, which I'll have to look into.

image

yiliansource avatar Jun 26 '21 09:06 yiliansource

Still actual!

azinit avatar Jun 26 '21 23:06 azinit

@slorber I'm afraid this is a little above me, I managed to get the generation working perfectly on my local machine, but deployment (even just for development testing on Vercel) has too many intricacies and limitations for me to handle.

yiliansource avatar Jun 29 '21 10:06 yiliansource

NP, I'll handle that later.

Afaik for Vercel free plan we have to reduce memory limit to 1024 or something, had to do this conf in the past: https://github.com/slorber/og-image-quotes/blob/master/now.json

slorber avatar Jun 30 '21 15:06 slorber

For later usage: GitHub has published a blog post on how they generate their social cards:

https://github.blog/2021-06-22-framework-building-open-graph-images/

slorber avatar Jun 30 '21 15:06 slorber

Hey @slorber,

I managed to get a working instance of this social cards feature working on my own site. I used the vercel og image tool. It works a charm. A good idea for this as @Josh-Cena was telling me about, is to have a config option to set your URL of your own vercel og image instance or even as you mentioned here calling it docusaurus-image and docusaurus can add that.

A way I have done it is by adding this file: here

What are your thoughts on this design?

PatelN123 avatar Feb 14 '22 13:02 PatelN123

@PatelN123 ideally you should not need to use swizzle on the Seo component to provide the social cards feature

Also, we want it to be flexible, so ideally the user should be able to provide a callback directly in the config to generate the social card URL, adapting to any card service (not just the one we suggest).

Eventually, we could even provide a lightweight package that builds social cards as part of the build process locally

slorber avatar Feb 16 '22 11:02 slorber

I would like to work on this.

I was able to get passing in a function working, but only inside of the main docusaurus.config.js, but not inside the theme config. Inside of the theme config, the function is evaluated to a string rather than returning a callback. I think that this is because theme data must be serialized to plain strings.

Is it fine to leave the function inside the main config?

dpang314 avatar Jun 15 '22 15:06 dpang314

@dpang314 yes, if it's actually accessed on server-side (and I hope it is!), you can put it in main config... for now. The actual API design can be deferred. A working prototype is fine.

Josh-Cena avatar Jun 15 '22 15:06 Josh-Cena

I have a proof of concept that is deployed here: https://capable-belekoy-059ce2.netlify.app/

Currently I haven’t fully implemented it, but it should work for all doc besides tag pages. It won't be difficult to apply the same implementation to tags, JSX/MDX, pages, and blogs.

A few examples: Page 1 | Preview 1 Page 2 | Preview 2 Page 3 | Preview 3 Category | Preview

I am generating the image url using the callback inside the doc plugin and adding it to doc metadata, which is then passed to the doc components that in turn pass the props to the existing <PageMetadata> component. I plan to do the same for the other plugins.

Will this implementation be feasible?

dpang314 avatar Jun 18 '22 04:06 dpang314

@dpang314 I'd like to see the code to be able to tell :)

But that looks to be working so that's a goot start πŸ‘

What matters most is to have a good/flexible public API.

If it's not possible to find the best API today, we can still ship this as an experimental version and do breaking changes later if we have a better approach.

slorber avatar Jun 22 '22 12:06 slorber

Link to the code. I have a lot to clean up and add, but the basic implementation is there.

dpang314 avatar Jun 22 '22 16:06 dpang314