A SEO meta tag generator for Fornax
enhance the search engine visibility of Fornax-generated websites with:
structured data in JSON-LD format
OpenGraph <meta>
tags
try to enforce some SEO best practises, e.g. requiring absolute URLs to all content items
NOTE
The following requires fornax
0.15.1 or newer.
Visit the wiki to learn how to use this package with earlier fornax
versions.
fornax new
paket
: dotnet tool install paket
dotnet paket init
Configure dependencies, e.g. at minimum:
|
dotnet paket install
IMPORTANT
Provide the root domain of your website:
// loaders/globalloader.fsx
#load @"../.paket/load/net8.0/Fornax.Core.fsx"
type SiteInfo = {
title: string
/// The root domain of your website - must be an absolute URL
baseUrl: string
description: string
}
Add personal authorship details, e.g.:
// loaders/globalloader.fsx
#load @"../.paket/load/net8.0/Fornax.Seo.fsx"
open Fornax.Seo
let loader (projectRoot: string) (siteContent: SiteContents) =
let siteInfo =
{ title = "Sample Fornax blog"
baseUrl = "http://example.com"
description = "Just a simple blog" }
let onTheWeb =
[ "linkedin.com/in/username"
"github.com/username"
"bitbucket.org/username"
"facebook.com/username" ]
let siteAuthor: ContentCreator =
{ Name = "Moi-même"
Email = "info@example.com"
SocialMedia = onTheWeb }
siteContent.Add(siteInfo)
siteContent.Add(siteAuthor)
siteContent
// generators/post.fsx
#load @"../.paket/load/net8.0/Fornax.Seo.fsx"
#load @"layout.fsx"
open Html
open Fornax.Seo
let generate' (ctx: SiteContents) (page: string) =
let siteInfo = ctx.TryGetValue<Globalloader.SiteInfo>()
let siteName = siteInfo |> Option.map (fun si -> si.title)
let tagline =
siteInfo
|> Option.map (fun si -> si.description)
|> Option.defaultValue ""
let siteAuthor =
ctx.TryGetValue<ContentCreator>()
|> Option.defaultValue ContentCreator.Default
let siteRoot =
siteInfo
|> Option.map (fun si -> si.baseUrl)
|> Option.defaultValue ContentObject.Default.BaseUrl
let post =
ctx.TryGetValues<Postloader.Post>()
|> Option.defaultValue Seq.empty
|> Seq.find (fun p -> p.file = page)
let postMeta: ContentObject =
{ Title = post.title
BaseUrl = siteRoot
Url = post.file.Replace(System.IO.Path.GetExtension post.file, ".html")
Description = tagline
Author = { siteAuthor with Name = defaultArg post.author siteAuthor.Name }
SiteName = siteName
Headline = Some post.summary
ObjectType = Some "Blog"
ContentType = Some "BlogPosting"
OpenGraphType = Some "article"
Locale = Some "en-us"
Published = post.published
Modified = post.modified
Tags = Some post.tags
Meta =
Some [ ("Image", defaultArg post.image $"{siteRoot}/images/avatar.jpg")
("Publisher", defaultArg siteName siteAuthor.Name) ] }
ctx.Add(postMeta)
// . . .
// generators/layout.fsx
#load @"../.paket/load/net8.0/Fornax.Seo.fsx"
open Html
open Fornax.Seo
// . . .
let layout (ctx: SiteContents) (active: string) (content: HtmlElement seq) =
let siteAuthor =
ctx.TryGetValue<ContentCreator>()
|> Option.defaultValue ContentCreator.Default
let seoData =
ctx.TryGetValues<ContentObject>()
|> Option.defaultValue Seq.empty
let pageMeta =
seoData
|> Seq.tryFind (fun p -> p.Title.Contains(active))
|> function
| Some info -> info
| _ -> { ContentObject.Default with Author = siteAuthor }
html [] [
head [] [
meta [ CharSet "utf-8" ]
meta [ Name "viewport"; Content "width=device-width, initial-scale=1" ]
// . . .
yield! seo pageMeta
]
body [] [
// . . .
footer [] [ yield! socialMedia siteAuthor ]
]
]
// . . .
A guide to building the project and making pull requests can be found here.
Distributed under the terms of the Mozilla Public License Version 2.0.