10 Elements of Highly Effective SaaS Landing Pages

How can you build a Software as a Service (SaaS) website? A website which is an effective tool to acquire customers? This article lines out advice from Rob Walling and Mike Taber from their podcast Startupsfortherestofus, “Episode 256 – The 10 Elements of Highly Effective SaaS Landing Pages”.

Before diving into this advice, remember that this is just about the technicalities of the landing page.  Even more important is to write for the benefit of the user/reader/customer.  A very good introduction is Amy Hoy’s post “6 Critical Mistakes You’re Making with Your Landing Page” .

The method described below – using your main website as a landing page for new visitors – is targeted at smaller SaaS companies. If you are Google or Trello, a different set of rules apply. As with most advice, the method presented below depends on the context.


10 elements for effective SaaS landing pages

The elements are described as a form of recipe (or algorithm for the developers under us) to get to an effective landing page easily. These elements go from the top of the page to the bottom. It is a prescriptive order of starting with the headline, having a visual element either next to it or below. And then go on to elements 4, 5, … Once you have everything in place, and your website is live, observe, analyse and adjust to the actual visitors and users you get.

Design the landing page for first time visitors.

You want to guide the visitor down a specific path. For first time visitors, try to educate them, get their interest, have them figure out if it’s for them, and then get them to take the next step in the action. “The purpose of your website is not to get them to buy your app, it’s to get them to come back to your website.” Don’t make your login box prominent, your existing users will know how to log in.

Gripping headline.

Write your headline with this simple, three part formula:

  • make a promise in the headline
  • have an action word, e.g. a verb
  • either have a directly stated “You” or “implied you”, talk directly to the person reading the headline

Here’s an example headline from hittail.com: “Guaranteed to increase your organic traffic” and the promise is that it’s guaranteed to increase organic traffic, the action (verb), is “Increase”, and then there’s a “You”, it’s “your”. Don’t talk about your app! Talk about the user. Another example headline for HitTail that’s less good: “The best long tail SEO keyword tool.” It’s worse because there’s no promise, no action and no you.

Have at least one visual element at the top of the page.

You can use a short video, less than 90 seconds, or an image. For example an image that describes what your app does just with a couple of circles and some arrows edited onto a screenshot of your app. Don’t use an image of a random person sitting there staring at a screen.

Another option is to have an e-mail capture form related to the functionality of your app. That’s a lot harder to do, because most apps don’t have this functionality. The e-mail capture must actually start a demo of the functionality of your app. An example of this is the bidsketch.com homepage. There is a form where you enter your e-mail, and it sends you a proposal basically demoing the functionality of the app.

Benefits.

List your app’s benefits using the rule of three, either 3, 6 or 9 benefits. The fewer benefits, the better. An example benefit is “Get more estimates”, together with one sentence describing what that means. Another example is “Deliver better projects and grow your business”. If your app serves separate, identifiable market segments like SEOs, internet marketers, and e-commerce, you can have multiple sets of benefits, one set for each specific market segment.

Keep your benefits down to earth. The biggest mistake with benefits is to write them so high level that it doesn’t make sense, like “Saves you time, makes you more money”. This is to generic, because every app does this. Be specific, write to the goals your users have, e.g. “Win more estimates.”

If you have trouble stating benefits, it’s often because of a confusion between features and benefits. For example if you make a list of benefits and a lot of them seem like features, you then take that feature and say: “We built XYZ feature so that …” “So that” is the key phrase here and by completing that sentence you transform the feature into a benefit statement.

Social proof.

A Social proof element is not optional. Most SaaS websites use social proof because it is known that it enhances your app’s credibility. When you have many (happy) users, it’s quite easy to get social proof. However it’s hard when you get started. Social proof takes different forms:

  • Testimonials from customers, preferably with head shots and the name linked to their website. Don’t have anonymous testimonials. Make sure to have the testimonial edited down to just the core part, 10 to 15 words maximum.
  • Press logos, like a logo and “featured on …”, “used by …”.
  • Vanity metric: If your app has analysed a billion keywords, sent out $100 million of proposals, … You can be creative here, as long the metric is directly related to the overall benefits of your app.

Features (optional).

Traditionally, a list of features was central to most landing pages. Nowadays such a list is mostly placed on a “Features” page. When you put features on the landing page, use very specific features that set your app apart. And specifically say “No other app has this feature.” or “These are the features that our customers like most.” On the landing pages features are optional, but you have to be very specific somewhere by linking to a tour or a features page with the specific mechanics of your app. Because if you’re just talking about benefits, no one knows what your app actually does.

Top nav with four or fewer items.

The top navbar or menu should just include four or fewer items (including your home link!). For example you could have “Home” on the left. The next item would be one of “Tour”, “How it works”, “Features”, explaining the basic flow and workings of your app. Then you should have a link to “Pricing”. Optionally you can have one of “Why [App Name]?” or “Contact”.

Have an exit path at the bottom.

Having an exit path means having e.g. a button to a next step you want the user to take. It could a specific journey that you are leading a visitor through (“Home”, “Tour”, “Pricing”, “Free Trial”). Have one main and maybe a secondary call to action at the bottom of your page. By not having this exit path at the bottom, it makes your reader get to the bottom, look around, probably click on something in your footer or scroll all the way back to the top and then you’ve lost them. An alternative is to embedded a call-to-action or exit path in the text in the middle/end of the page, if it makes sense from a visitor flow perspective. Make such an exit obvious by e.g. using a big orange button that says “Increase your traffic” or “Take a tour”.

Such an exit path should be on all pages, not just the Home page.

Limit the number of links and buttons.

Basically limit the number of links and buttons that you have on the page. This limits the number of decisions that visitors need to make as they’re reading and navigating your landing page.

Put everything else into your footer.

Your app’s footer should contain links to important pages like “About”, “Contact”, “Terms of service”, “Blog”, “Affiliates” etc. Don’t put an e-mail sign-up form in the footer. Nobody will use it. It’s much better to use a little JavaScript widget and have it pop up at certain times and control when to make it visible and when not to. At last, a sitemap may make sense from an accessibility and SEO point of view.

Further reading


How to remove mojibake from mysql dump files

Sometimes a mysql dump file contains latin-1 text encoded wrongly as UTF-8 unicode encoding. That leads to some characters out of the ASCII range being garbled and displayed as two UTF-8 characters. This is called mojibake. E.g. “l’Oreal” for “L’Oreal”.
When you dump such a database using:

mysqldump --opt -h localhost -u dbuser -p db > dump.sql

You get the mojibake in this dump file.

How to correct the mojibake

Luckily there is a python3 tool called ftfy to remove mojibake and replace it with the garbled characters.  Ftfy is a nifty work of programming and educated guesswork, because ftfy must guess what the original encoding was, and which transformation to apply to the dump file.  To make this guessing work, ftfy uses a line by line approach to guessing any encoding mistakes.  However this approach does not work with standard mysqldump files, because they can contain extremely long lines. This causes the ftfy guessing algorithm to not work effectively, because the algorithm assumes that lines are not very long in order to guess if there are wrong character sequences in a line.

The right mysqldump

Reduce the length of the lines in mysqldump by using the command line parameter –skip-extended. This parameter writes multiple SQL INSERT statements per table, such that each INSERT is on a new line. The drawback is that dumping and restoring a database is slower.

mysqldump --opt --skip-extended -h localhost -u dbuser -p db > dump.sql

This dump still contains the mojibake and is now ready to be processed further.

Using ltfy

First install lftfy:

virtualenv -p python3 venv
source ./venv/bin/activate
pip install ftfy

Then prepare a file to call ftfy on your mysql dump file (with thanks to Pielo):

import ftfy
# Set input_file
input_file = open('dump.sql', 'r', encoding='utf-8')
# Set output file
output_file = open('dump.utf8.sql', 'w')

# Create fixed output stream
stream = ftfy.fix_file(
	input_file,
	encoding=None,
	fix_entities=False,
	remove_terminal_escapes=False,
	fix_encoding=True,
	fix_latin_ligatures=False,
	fix_character_width=False,
	uncurl_quotes=False,
	fix_line_breaks=False,
	fix_surrogates=False,
	remove_control_chars=False,
	remove_bom=False,
	normalization='NFC'
)

# Save stream to output file
stream_iterator = iter(stream)
while stream_iterator:
	try:
		line = next(stream_iterator)
		output_file.write(line)
	except StopIteration:
		break

Then you just need to call:

python dbconvert.py

Thereafter you can just restore the dump file into mysql:

mysql -h localhost -u dbuser -p db < backup.sql

Beware of duplicates introduced by removing mojibake in SQL

Sometimes removing the mojibake can result in duplicate rows in the database, even when there was a UNIQUE KEY constraint or UNIQUE index.  The reason is that different two character encodings for a single UTF-8 character can be recognized by ftfy.  This then leads to duplicate rows.

Normalize Python DB API calls between SQLite and MySQL/PGSQL

Somehow there is no single formatting for parameters/variables in python DB API calls.  Looking at the DB API specification, it seems that specific database drivers can be written  with ‘?’ or ‘%s’, or even other conventions.

paramstyleMeaning
qmarkQuestion mark style, e.g. ...WHERE name=?
numericNumeric, positional style, e.g. ...WHERE name=:1
namedNamed style, e.g. ...WHERE name=:name
formatANSI C printf format codes, e.g. ...WHERE name=%s
pyformatPython extended format codes, e.g. ...WHERE name=%(name)s

How do you know the ‘paramstyle’ of your database connection?  For SQLite use:

>>> import sqlite3
>>> sqlite3.paramstyle
'qmark'

Using a library like sqlalchemy circumvents this problem.  In case you need to use the DB API drivers, a simple function which formats SQL strings can be used to enhance portability. The function below does this. It is a quick 80% solution. Adapt as needed.

def fs(sql_string):
""" format sql string according to db engine used """
# normalize dbapi parameters, always use %s (MySQL, PG) in sql_string,
# set escape_string to r'?' for sqlite
# e.g. %s and ?
# escape_string = r'%s '
escape_string = r'? '
# autoincrement_string = "AUTO_INCREMENT" # for MySQL
autoincrement_string = "AUTOINCREMENT" # for sqlite
return_string = sql_string
return_string = re.sub(r'%s ', escape_string, return_string)
return_string = re.sub(r'AUTO_INCREMENT', autoincrement_string, return_string)
return return_string

It is possible (and likely) that the SQL for the DDL will be different between e.g. SQLite and MySQL.  For example, in MySQL you’d use ‘AUTO_INCREMENT’, whereas in SQLite you’d use ‘AUTOINCREMENT’.  By extending the above approach to also replacing these strings, you can further abstract the database code.