Better SEO with GWT

When we started working on our new website, we quickly faced challenges often encountered with single-page application frameworks: enabling search engine optimization (SEO) and crawlability. The problem with these kinds of applications is that you only have one .html host file, which has two important consequences:

  1. Having only one title tag makes it hard for people to use the browser history for navigation
  2. It becomes impossible to define meta properties with relevant data for different site sections

Thankfully, each of these problems can be solved.¨

Changing the title

This is actually a simple fix. We can use GWT’s Document class to change the title when our section is attached to the DOM:

Adding and updating meta properties

The hard way

We could create MetaElements and inject them into the head section manually when our section is attached to the DOM :


This seems a bit cumbersome and isn’t quite what webmasters are used to, so perhaps we can do better. This is where GWT-SEO comes into play. It allows us to define meta properties in the UiBinder’s ui.xml file as a Widget, almost like we would define them in an html file. The library will then create and inject the defined meta elements in the head section when your View is attached to the DOM, removing the need to register your own AttachHandler.

Now that’s better! Notice that the title is also handled, which means no more document.setTitle() is needed either!

Internationalization (i18n)

Moreover, since we’re within a UiBinder, we can therefore take advantage of the power of ui:msg to internationalize our properties (more details on using GWT i18n with UiBinder in an upcoming post!). In the meantime, you can take a look at our website for a live example of GWT-SEO using i18n, and especially at the sources of the website to see more in details how it was done, or at the source of my bee page for a specific example.

Dynamic data

More often than not, when implementing a single-page application, the content of the section will depend on some data fetched from an AJAX request (e.g. : a product page in an online store). GWT-SEO can also help you with that, although it requires some Java code in your request’s callback. Let’s say we have a Presenter that retrieves a Product when the product’s dynamic page is loaded, and passes it to its View through the setProduct method.

The advantage of using GWT-SEO, versus manually creating MetaElements to inject dynamic properties, is that it prevents duplicates by replacing existing properties with new ones, by leveraging the injection of derived tags (title and og:title, description and og:description, etc.).

But what good would all that do if crawlers like Google or Facebook can’t retrieve the fully rendered version of the page?

Making the application crawlable

Making your application crawlable could be the topic of a whole other blogpost, but I will discuss it briefly here.

Most crawlers nowadays implement a simple directive that allows us to serve different content when a hashbang (#!) is encountered in an URL. So the first step is to make sure your section URLs contain hashbangs. For example, a crawler fetching!/support would replace the hashbang with the escaped_fragment query parameter, in this case : (take a look at the rendered DOM).

Rendering for crawlers

Now that we have a way to identify crawlers’ requests, we need to serve a rendered version when we detect the _escaped_fragment_ query parameter to ensure the crawler doesn’t need to run any JavaScript code to access the content. To achieve this, we need something like HtmlUnit or PhantomJS to retrieve our webpage and process the JavaScript until we have a fully rendered page. Or better yet, use GWTP Crawler Service, which handles everything for us, from detecting crawlers’ requests to generating a fully rendered page!


Writing the meta properties for all the sections of your single-page application will allow you to be better ranked on search engines, and give you a chance to stand out against your competitors. It will also make your pages easier to share across social medias, which is, in this 2.0 era, mandatory for any web page. SEO and crawlability are easy things to overlook when developing your application, but to ignore them is to miss out on a substantial number of potential visitors!


  • Marc Traver Serra

    Hi Maxime, need more information about two points:

    1) Using GWT-SEO wihout UIBinder, instead of this with the “old” extend Composite classes.
    2) Making GWT crawlable with a practical example.

    Maybe this information is already published on other posts, but I can’t find it, so I hope, at least, you can gave me those links 😉

    Thanks a lot in advance and thanks a lot for your practical-oriented post!

    • Maxime Mériouma-Caron

      Hi Marc,

      1) Please refer to the Dynamic Data section. It manually inject Seo elements with the TagsInjector class

      2) You can take a look at the source code of our website : We use GWT-SEO and a crawling servlet to render the pages.

      Hope this helps!

  • Julien GUIBERT

    Bonjour Maxime,

    J’ai suivi tes instructions pour la mise en place du SEO, mais je bloque sur des erreurs avec la classe DispatchServiceImpl.

    Ma classe DispatchServletModule :

    protected void configureServlets() {
    serve(“/” + ActionImpl.DEFAULT_SERVICE_NAME + “*”).with(DispatchServiceImpl.class);

    Ma classe ClientModule :

    public class ClientModule extends AbstractPresenterModule {

    protected void configure() {
    DefaultModule defaultModule = new DefaultModule.Builder().build();
    install(new ApplicationModule());

    J’ai l’erreur suivante dans AppEngine :

    Uncaught exception from servlet Guice configuration errors:

    1) No implementation for com.gwtplatform.dispatch.rpc.server.Dispatch was bound.
    while locating com.gwtplatform.dispatch.rpc.server.Dispatch
    for parameter 1 at com.gwtplatform.dispatch.rpc.server.guice.DispatchServiceImpl.(
    while locating com.gwtplatform.dispatch.rpc.server.guice.DispatchServiceImpl

    2) No implementation for com.gwtplatform.dispatch.rpc.server.RequestProvider was bound.
    while locating com.gwtplatform.dispatch.rpc.server.RequestProvider
    for parameter 2 at com.gwtplatform.dispatch.rpc.server.guice.DispatchServiceImpl.(
    while locating com.gwtplatform.dispatch.rpc.server.guice.DispatchServiceImpl

    Merci pour ton aide.

    • Maxime Mériouma-Caron

      Bonjour Julien,

      Votre problème semble plutôt lié au RPC-Dispatch de GWTP, et non au Crawler ou au SEO. Je vous suggère de jeter un oeil à ceci pour vérifier si le RPC-Dispatch est configuré correctement :

      • Julien GUIBERT

        J’ai la même configuration que la section “Using RPC Dispatch with Guice”. Seul GuiceServletConfig diffère, j’instancie seulement DispatchServletModule :

        public class GuiceServletConfig extends GuiceServletContextListener {
        protected Injector getInjector() {
        return Guice.createInjector(new DispatchServletModule());

        La configuration serveur doit être suffisante ? Je ne dois pas utiliser RpcDispatchAsyncModule ?

        Je cherche 🙂

        Merci encore

        • Maxime Mériouma-Caron

          Hmm effectivement, il semble manquer de l’information. Il faut installer le DispatchModule à l’aide du Builder : install(new DispatchModule.Builder().build())

          • Julien GUIBERT

            Merci Maxime, ça marche, je vais tester le SEO.

  • Julien GUIBERT


    Est-ce que vous connaissez une méthode alternative pour le crawling GWT, sachant que la spécification est obsolète :

    Dans l’idéal, je souhaiterai crawler mes pages dynamiques “produit” et référencer juste le title et la description.