Discourse does not produce large, more engaging Twitter cards

Top: How Discourse produces a not very much engaging so-called “mini card.”

Bottom: Correct, large Twitter card.


Please change…

<meta name="twitter:card" content="summary" />


<meta name="twitter:card" content="summary_large_image">

in core.



Hi @Terrapop

I don’t think that’s a solution. Sometimes images are bad. And bad images with summary_large_image are really bad.

May be an option so the user can change summary to summary_large_image.


In my case, I would opt for that as we make sure that images added to the topics we Tweet on our account have large enough images. AFAIK Twitter automatically falls back to the small version, if you don’t provide a large enough image even when summary_large_image is provided.

Even better would be if images sizes are vetted by core and depending on the minimum sizes required use summary or summary_large_image (min 300x157) automatically.

If that is not possible at all, I would certainly be happy to at least have the option to use summary_large_image instead of summary.


Is there any way to overwrite the behavior of def crawlable_meta_data with a simple plugin?

In particular, we would like to change:

elsif opts[:image].present?
      result << tag(:meta, name: 'twitter:card', content: "summary")
      result << tag(:meta, name: "twitter:image", content: opts[:image])


elsif opts[:image].present?
      result << tag(:meta, name: 'twitter:card', content: "summary_large_image")
      result << tag(:meta, name: "twitter:image", content: opts[:image])

as we’re pretty depending on Twitter and big cards for engagement.

I’m quite new to Ruby, so any nudge into the right direction would be appreciated.

Update: Here is the plugin.rb for those who want to patch core because of the small images on Twitter:

after_initialize do
 reloadable_patch do |plugin|
  ApplicationHelper.module_eval do

    def crawlable_meta_data(opts = nil)
        opts ||= {}
        opts[:url] ||= "#{Discourse.base_url_no_prefix}#{request.fullpath}"

        if opts[:image].blank?
          twitter_summary_large_image_url = SiteSetting.site_twitter_summary_large_image_url

          if twitter_summary_large_image_url.present?
            opts[:twitter_summary_large_image] = twitter_summary_large_image_url

          opts[:image] = SiteSetting.site_opengraph_image_url

        # With and height available?

        # Use the correct scheme for opengraph/twitter image
        opts[:image] = get_absolute_image_url(opts[:image]) if opts[:image].present?
        opts[:twitter_summary_large_image] =
          get_absolute_image_url(opts[:twitter_summary_large_image]) if opts[:twitter_summary_large_image].present?

        # Add opengraph & twitter tags
        result = []
        result << tag(:meta, property: 'og:site_name', content: SiteSetting.title)

        if opts[:twitter_summary_large_image].present?
          result << tag(:meta, name: 'twitter:card', content: "summary_large_image")
          result << tag(:meta, name: "twitter:image", content: opts[:twitter_summary_large_image])
        elsif opts[:image].present?
          result << tag(:meta, name: 'twitter:card', content: "summary_large_image")
          result << tag(:meta, name: "twitter:image", content: opts[:image])
          result << tag(:meta, name: 'twitter:card', content: "summary")
        result << tag(:meta, property: "og:image", content: opts[:image]) if opts[:image].present?

        [:url, :title, :description].each do |property|
          if opts[property].present?
            content = (property == :url ? opts[property] : gsub_emoji_to_unicode(opts[property]))
            result << tag(:meta, { property: "og:#{property}", content: content }, nil, true)
            result << tag(:meta, { name: "twitter:#{property}", content: content }, nil, true)

        if opts[:read_time] && opts[:read_time] > 0 && opts[:like_count] && opts[:like_count] > 0
          result << tag(:meta, name: 'twitter:label1', value: I18n.t("reading_time"))
          result << tag(:meta, name: 'twitter:data1', value: "#{opts[:read_time]} mins 🕑")
          result << tag(:meta, name: 'twitter:label2', value: I18n.t("likes"))
          result << tag(:meta, name: 'twitter:data2', value: "#{opts[:like_count]} ❤")

        if opts[:published_time]
          result << tag(:meta, property: 'article:published_time', content: opts[:published_time])

        if opts[:ignore_canonical]
          result << tag(:meta, property: 'og:ignore_canonical', content: true)



But it would be better if there would be an option for this going forward and if core would assess min Twitter images sizes to either do summary_large_image or summary (for very small images) if a admin opts for a new setting: “enable large Twitter summary images”.


I would like to know if it’s possible to change the current behavior of always showing summary_large_image to summary — I came across this post but it appears that it is asking for the reverse — so I guess that someone must have implemented it… however is it possible to do the reverse? Is there an option? I tried searching for Admin settings and can’t find it.

1 Like