Series: Building a Social Network with Flask & Stream – Part 10

This segment is the tenth installment of a tutorial series focused on how to create a full-stack application using Flask and Stream. In this article, we are going to start styling our app and adding cool new features like notification counts and link previews. Be sure to check out the Github repo to follow along!

Getting Started

As of our previous tutorial, we have built all of the basic functionality of our site. Our last issue to resolve is that the app lacks any kind of aesthetic appeal. Currently, any links that a user might add do not have any visual flare- the feeds definitely won't be winning any UI design awards. One thing that would drastically improve the look of our site would be to add link previews, similar to Facebook, Twitter, or Pinterest. Also, being able to auto-populate the title and description fields for the user after they enter a link would make for a much better user experience.


Luckily, Stream provides a very convenient, asynchronous JavaScript function that takes a URL and returns all of the information that we need using Open Graph. Open Graph protocol returns titles, descriptions, images, and videos from a link. We already have the description and title, so the only thing left to add is images. Feel free (even encouraged!) to add a conditional into your app to allow videos to display instead of images if they are available.

However, before we can start integrating this functionality, we need to make space for it in our web app. The first step is adding images to our database model, as well as providing a way to update our Stream Feeds for new fields (in app/

Hindsight Is 20/20

Before we can start adding new content, any links to existing content do not have images associated with them yet. When we start using images in our feeds, some of them will display with images, and others will have a large, ugly “None” text showing instead. Therefore, we need a solution that allows us to update each image in the database as well as our Stream feeds. Since we are using Open Graph on the front end, the backend script should use the same thing. To do so, we will use the Python-OpenGraph library. To install it, run pip install python-opengraph. Then (in we will create a CLI script that returns all content entries in the database, queries their Open Graph values, returning and updating their image values for the table and Stream.

Again, we have altered our database model, so be sure to run flask db migrate, flask db upgrade, as well as the newly created flask update-images. The command line does not recognize underscores, so replace the underscore(_) with a dash(-) when running the command.

Picture Perfect

Now that everything is up to date, we can start to change our forms, views, and templates for the new field. We begin with the templates (in app/templates/_content.html).

Next, we need to adjust our index page for the home timeline to retrieve and render the content through the Stream request (in app/templates/index.html).

Finally, we do the same for the collection page (in app/templates/collection.html).

See It To Believe It

We want to give users the chance to see their content before they post it to a collection by creating a link preview in the new content screen. The preview needs to have a few bits of functionality. First, we want it to show and return the image, but we don’t want the image field itself to show. Second, we want the preview to reflect the on-screen input fields for title and description. We start this process off with our forms (in app/main/`)

I added ID parameters to each of the fields in the form, which help us select them using jQuery in the template. I also created the image field as a hidden field so that it won’t show to the user.

We also need to update our new content view to return a token for the user (for the Stream function) as well as to provide the ability to use the new image field in creating a record from the form (in app/main/

Since the preview does not need nor have some of the information provided by the full content template, we need to create a new one (in app/templates/_content_preview.html).

The Stream Open Graph scraper works in a very similar way to the requests we have been doing for a while, so it is a straightforward crossover for our existing JavaScript code (in app/templates/new_content.html).

The input string variables at the top provide regex for two things. The first is for ‘dirty’ URLs, or when a link is surrounded by unrelated text, and the second identifies a ‘clean’ URL, one that does not have any surrounding text.

This page includes the addition of the onkeyup and onkeypress methods at the bottom which trigger an update to the preview at the top when the input fields are changed. Also, we use jQuery to set values for the form when the preview function is run, so import it in app\templates\base.html.

The last thing we have to do is update the content page itself to include the image (in app/templates/content.html).

Notify Me

Even though we have a notifications feed running, it does not provide the notification-count that you would expect to see from a modern social media platform. Luckily, we can put this together in a snap. Since the notification-count displays in the navbar, we need to create a badge (like our follower-count) that visibly shows the count of unseen notifications (in app/templates/base.html).

Since the navbar returns on every page, we need to provide a current user ID # to the Stream request to get the count. While I am going to pass through the current user token on every rendered template, you could just as easily modify the code to use cookies instead.

Finally, we want the count to return to zero once a user opens the notification page, as that would indicate they have, in fact, seen the notifications. This step requires a very simple tweak to accomplish (in app/templates/notifications.html).

Gotta Have Style

We are getting close to the end now! The last thing that we need to tackle is some simple CSS changes to polish the look of the site. First, let's take a look at changing the font. I personally prefer to stick to one or two fonts at an absolute maximum, as font-weight, style, and decoration can provide all of the variation you actually need. In the case of this demo, I chose Montserrat from Google Fonts, but feel free to play around and find what works best for you. We can import the font in app/templates/base.html.

Additionally, we have text notifying a user that they have reached the end of a feed. It would look cleaner and clearer if we replaced the text to use an image instead. We will have to update this in all of the templates that use feeds, starting with the home page (in app/templates/index.html).

Then notifications (in `app/templates/notifications.html)

Next, the user page (in app/templates/user.html)

And finally, the collection page (in app/templates/collection.html)


As you can see, our user page has some slight stylistic issues, particularly with the username and user’s name being displayed right beside each other. This appears redundant, so it would help if only one of these elements were displayed, preferably their actual name. Additionally, we need to add some tags to the page to add styles (in app/templates/user.html).

Final Touches

Now, all we have to do is put together our CSS for the page, and we are done!

We’start off by creating the CSS document and defining our element level references (in app/static/styles.css).

Next, we set a maximum width on our content container to limit the size of the pictures and nicely center the content (in app/static/styles.css).

After that step, we give some visual flair to the content elements, specifically the image. Creating an animated shadow for the image will add a nice-looking effect. Varying the font sizes and weights (but don’t go too crazy!) also helps to break up the text and allows the user to focus on different sections independently (in app/static/styles.css).

In continuation, we focus on the page headers, our profile, and content pages, centering the text and putting emphasis on the user’s name (in app/static/styles.css).

The notifications-count has a rather bland gray background, which undercuts its importance. A sleek-looking gradient effect can make it appear a lot more interesting (in app/static/styles.css).

Last but not least, we need to center and size the “finished” image we integrated this week (in app/static/styles.css).

Final Thoughts


You have officially created an entire web application with Flask and Stream, complete with styling and some rather slick functionality. The response times for our content is seamless thanks to lightning-quick data retrieval from Stream, with a modern, feed-based view to check out all the latest and greatest activities from your friends. We are getting very close to the end of this section of the tutorial, with our last step being deployment and configuration. In the next article, I am going to take you through deploying the entire app as a serverless function using AWS Lambda, RDS, and Amazon Mail!

As always, thanks for reading and happy coding!

Note: The next post in this series can be found here