Link Preview for Articles on Site

What I’m trying to do: I have a website that hosts different articles. When someone links to my site on something like LinkedIn/Facebook, I want it to show the title/content/image from that particular page not the site general info. Note: I use hashrouting.

What I’ve tried and what’s not working:
I’m actually not sure where to begin here. I naively tried to use this, but it did nothing:
from anvil.js.window import document
document.title = “TEXT”

Thanks for your support :slight_smile:

Welcome to the Forum!

I haven’t used hashrouting, but just from a data-available standpoint, your Anvil App is not going to automatically know much about where it was linked from – unless that’s passed, somehow, as part of the URL that invokes your App.

So, first, make sure that the necessary information is indeed being passed. If you could show a sample URL, for the hashrouting experts to see, they may be able to take it from there.

1 Like

Most of those sites use meta tags in the head section of the page. You might be able to simulate that with custom HTML forms, but it depends on whether they execute Javascript or not and other factors.

The first step is to pick a site you want to show a preview on, and then identify exactly which tags are required, and do a test by changing the standard-page.html contents in an app to see if the preview shows up the way you want.

If all that works, then you can look at if it’s possible to change it per form, which as Phil says will require hash routing, and also a lot of luck.

Those platforms also change their requirements fairly frequently, so those previews are generally a moving target. I worked on a media platform that provided Facebook previews when someone linked to a video on Facebook, and it was a pain to keep up with the changes.

Here is a workaround you can use using HTTP endpoints

@anvil.server.http_endpoint('/articles/:id')
def get_article(id):
    html="""
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>Article Title</title>
    <meta name="description" content="Article Description">
    <link rel="icon" type="image/png" href="Icon.png">
    
    <script>
    window.location.href="<The hash route to that article>"
    </script>
    """
    return anvil.BlobMedia('text/html',bytes(html.encode()))
1 Like

Hey, so just to be clear what you’re recommending here…

Currently my hashrouting sends visitors to this URI:
@routing.route(‘research/{uid}’) → “*.com/#research/{uid}”

So would I modify the html to '/#research/ '+ uid (Not sure if leading ‘/’ is necessary)?

I assume I would want to change the endpoint to align with /#research/:id as well?

Thanks for your help!

Forgive me for not clarifying my solution (I was in a bit of hurry back then). But the solution requires creating HTTP endpoints in your Server Module.

So for example, you have an article available at https://anything.anvil.app/#research?id=1

But when a user wants to share it, you will instead provide them a link to https://anything.anvil.app/_/api/research/1

And then you can create a HTTP endpoint in your Server Module link this

@anvil.server.http_endpoint('/research/:id')
def get_research(id):
    row=app_tables.research.get(id=id) #Finding the row for that research using the provided ID
    title=row['Title']
    description=row['Description']
    image=row['Image'].url
    hash_url = f"https://something.anvil.app/#research?id={id}"
    html=f"""
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{title}</title>
    <meta name="description" content="{description}">
    <link rel="icon" type="image/png" href="{image}">
    
    <script>
    window.location.href="{hash_url}"
    </script>
    """
    return anvil.BlobMedia('text/html',bytes(html.encode()))
3 Likes

Hello! I just want to say this has been working quite well. For some reason I can’t get the image portion to work, and I was wondering if you had any insights. I’ve tried using it from a public link and from the media url from the db on anvil.

Can you provide me the link which doesn’t work?

https://i.ibb.co/cTb8W98/sec.png

Provide me the link of the article which is having trouble displaying images

https://secdose.com/_/api/blog/30_Blog

Any that hit this endpoint

I don’t see anything wrong with this. I just uploaded it here.
Although it does take time for the preview to load.

But it doesn’t show an image

Oh, I see the problem. Right now, you are only setting the image as the favicon for the url. There is an additional tag you can add for displaying a proper sized image.

@anvil.server.http_endpoint('/research/:id')
def get_research(id):
    row=app_tables.research.get(id=id) #Finding the row for that research using the provided ID
    title=row['Title']
    description=row['Description']
    image=row['Image'].url
    hash_url = f"https://something.anvil.app/#research?id={id}"
    html=f"""
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{title}</title>
    <meta name="description" content="{description}">
    <meta property="og:image" content="{image}" />
    <link rel="icon" type="image/png" href="{image}">
    
    <script>
    window.location.href="{hash_url}"
    </script>
    """
    return anvil.BlobMedia('text/html',bytes(html.encode()))

This will be the result of it

2 Likes

Not sure what I’m doing wrong here :confused:

This is the snippet:

title=story['title']
description=story['p1'][0:190] + '...'
image="https://i.ibb.co/cTb8W98/sec.png"
  hash_url = f"https://secdose.com/#blog/" + str(id)
  html=f'''
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>{title}</title>
    <meta name="description" content="{description}">
    <meta property="og:description" content=" content="{description}"/>
    <meta property="og:type" content="article" />
    <meta property="og:image" content="{image}" />
    <link rel="icon" type="image/png" href="{image}">
    
    <script>
    window.location.href="{hash_url}"
    </script>
    '''
  return anvil.BlobMedia('text/html',bytes(html.encode()))

Thank you for your help so far - really not sure what is preventing image from showing now but you have been really great

1 Like

Thanks for the guidance on this. I created this helper class based on the details in this post. I hope it helps someone.

P.S. I have not tested the Twitter and Pinterest components, as I do not have accounts on those sites. Please let me know if they work.

Code Here:
import anvil.server


class LinkPreview:
    def get_link_preview(
        self,
        title: str,
        description: str,
        hash_url: str,
        image_url: str = "",
        image_alt: str = "",
        twitter_site: str = "",
        twitter_handle: str = "",
        no_pin: bool = True,
        rich_pins: bool = True,
    ):
        """
        Gets a link preview with the given parameters.

        Args:
            title (str): The title of the link preview.
            description (str): The description of the link preview.
            hash_url (str): The URL of the link preview.
            image_url (str, optional): The URL of the image for the link preview. Defaults to "".
            image_alt (str, optional): The alt text for the image. Defaults to "".
            twitter_site (str, optional): The Twitter site for the link preview. Defaults to "".
            twitter_handle (str, optional): The Twitter handle for the link preview. Defaults to "".
            no_pin (bool, optional): If True, the link preview will not be pinned. Defaults to True.
            rich_pins (bool, optional): If True, the link preview will be a rich pin. Defaults to True.
        """
        pinterest_content = "nopin" if no_pin else ""
        rich_pins = "true" if rich_pins else "false"

        html = f'''<!DOCTYPE html>
                <html lang="en">
                <head>
                    <meta charset="utf-8">
                    <meta http-equiv="X-UA-Compatible" content="IE=edge">
                    <title>{title}</title>
                    <meta name="description" content="{description}">
                    <link rel="icon" type="image/png" href="{image_url}">
                    <meta property="og:title" content="{title}">
                    <meta property="og:description" content="{description}">
                    <meta property="og:image" content="{image_url}">
                    <meta property="og:image:alt" content="{image_alt}">
                    <meta property="og:site_name" content="{anvil.server.get_app_origin()}">

                    <!-- Twitter specific tags -->

                    <meta name="twitter:card" content="summary_large_image">
                    <meta name="twitter:site" content="{twitter_site}">
                    <meta name="twitter:creator" content="{twitter_handle}">
                    <meta name="twitter:title" content="{title}">
                    <meta name="twitter:description" content="{description}">
                    <meta name="twitter:image" content="{image_url}">
                    <meta name="twitter:image:alt" content="{image_alt}">

                    <!-- Pinterest specific tags -->

                    <meta name="pinterest" content="{pinterest_content}">
                    <meta name="pinterest-rich-pin" content="{rich_pins}">
                    <script>
                        window.location.href="{hash_url}"
                    </script>
                </head>
                <body>
                </body>
                </html>'''

        return anvil.BlobMedia("text/html", bytes(html.encode()))

    @classmethod
    def construct_link_preview(
        cls,
        title: str,
        description: str,
        hash_url: str,
        image_url: str = "",
        image_alt: str = "",
        twitter_site: str = "",
        twitter_handle: str = "",
        no_pin: bool = True,
        rich_pins: bool = True,
    ):
        return cls().get_link_preview(
            title=title,
            description=description,
            image_url=image_url,
            hash_url=hash_url,
            image_alt=image_alt,
            twitter_site=twitter_site,
            twitter_handle=twitter_handle,
            no_pin=no_pin,
            rich_pins=rich_pins,
        )