SEO
Intro
SEO (Search engine optimization) is to make web content readable and indexable by search engines such as Google.
Because app built with go-app are Go binaries loaded in the web browser and their content is generated dynamically on the client-side, it is tricky for search engines to crawl and index apps content.
Go-app is solving that issue by prerendering components on the server-side before including the generated HTML markup into requested pages.
Prerendering
Prerendering is about converting a component to its plain HTML representation.
On the server-side, when the Handler receives a page request, it creates an instance of the component associated with the requested URL, generates its HTML representation, and includes it on the page.
For a simple Hello world component:
type hello struct {
app.Compo
}
func (h *hello) Render() app.UI {
return app.H1().Text("Hello World!")
}
The generated page will look like the following code:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta httpequiv="Content-Type" content="text/html; charset=utf-8" />
<link rel="manifest" href="/manifest.webmanifest" />
<link type="text/css" rel="stylesheet" href="/app.css" />
<script defer src="/wasm_exec.js"></script>
<script src="/app.js" defer></script>
</head>
<body>
<div>
<!-- Prerendered component is included in the div below: -->
<div id="app-pre-render">
<h1>Hello World!</h1>
</div>
<div id="app-wasm-loader" class="goapp-app-info">
<img
class="goapp-logo goapp-spin"
src="https://storage.googleapis.com/murlok-github/icon-192.png"
id="app-wasm-loader-icon"
/>
<p id="app-wasm-loader-label" class="goapp-label">Loading...</p>
</div>
</div>
<div id="app-end"></div>
</body>
</html>
Customizing prerendering
Like on the client-side, a component might require further initializations. Launching additional instructions is done by implementing the PreRender interface.
Here is a Hello example that gets the username from an URL parameter:
type hello struct {
app.Compo
name string
}
func (h *hello) OnPreRender(ctx app.Context) {
username := ctx.Page.URL().Query().Get("username")
h.name = username
}
func (h *hello) Render() app.UI {
return app.H1().Text("Hello " + h.name)
}
Customizing page metadata
An essential step for a good SEO is to have meta tags, such as page title, well-formed.
Page metadata can be set from the OnPreRender
Context argument. Context contains a page field to set page metadata. Here is an example that sets the page title and author.
func (h *hello) OnPreRender(ctx app.Context) {
ctx.Page.SetTitle("A Hello World written with go-app")
ctx.Page.SetAuthor("Maxence")
}
See the page reference for the detail of customizable metadata.
Caching
For performance reasons, prerendered pages are cached once generated.
The Handler provides a PreRenderCache
field that allows customizing the cache behavior. By default, it uses an in-memory LRU cache that keeps cached data for 24 hours with a maximum size of 8MB.
Cache behavior can be customized by setting the PreRendering cache to another LRU cache with different values:
h := app.Handler{
Name: "Hello world",
PreRenderCache: app.NewPreRenderLRUCache(100*100000, time.Hour), // 10MB/1hour
}
Or any other cache that satisfies the PreRenderCache interface:
type PreRenderCache interface {
// Get returns the item at the given path.
Get(ctx context.Context, path string) (PreRenderedItem, bool)
// Set stored the item at the given path.
Set(ctx context.Context, i PreRenderedItem)
}
You could implement a cache based on Redis or any other datastore.