### CI/CD Setup for Seoslug Static Manifest
Source: https://seoslug.emiliano-go.com/integrations/nuxt
Example GitHub Actions workflow for setting up Python, installing Seoslug and PyYAML, generating SEO data, and running an npm build.
```yaml
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- run: pip install "seoslug>=2.0.1" pyyaml
- run: python scripts/generate_seo.py
- run: npm ci
- run: npm run generate
```
--------------------------------
### Sub-path Deployment Example Configuration
Source: https://seoslug.emiliano-go.com/url-normalization
Example of configuring SEOConfig and URLPolicy for a sub-path deployment, ensuring canonical URLs work correctly.
```python
from seoslug import SEOConfig, URLPolicy
config = SEOConfig(
canonical_host="example.com",
public_base_url="https://example.com/blog",
url_policy=URLPolicy(
lowercase_paths=True,
trailing_slash="never",
collapse_duplicate_slashes=True,
strip_tracking_params=True,
),
)
# Given route "/about"
# Result: "https://example.com/blog/about"
# Given route "/"
# Result: "https://example.com/blog"
```
--------------------------------
### Install seoslug with Development Dependencies
Source: https://seoslug.emiliano-go.com/contributing
Clone the repository and install the project in editable mode with testing and fast extras for development.
```bash
git clone https://github.com/emiliano-go/seoslug.git
cd seoslug
pip install -e ".[test,fast]" # or ".[test,light]" for minimal
```
--------------------------------
### Install seoslug with Minimal Dependencies
Source: https://seoslug.emiliano-go.com/faq
Install a minimal, pure-Python version of seoslug without lxml or detrack using the `[light]` extras marker.
```bash
pip install "seoslug[light]"
```
--------------------------------
### Install seoslug with Fast Extras
Source: https://seoslug.emiliano-go.com/getting-started
Install the seoslug library with lxml and detrack for optimized HTML parsing and tracking-parameter stripping. Requires Python 3.10 or later.
```bash
pip install "seoslug[fast]"
```
--------------------------------
### Example Usage of SEOOverrides
Source: https://seoslug.emiliano-go.com/api-reference
Demonstrates how to instantiate the SEOOverrides dataclass with specific values to customize SEO metadata and control template usage.
```python
overrides = SEOOverrides(
meta_title="Custom Title",
skip_title_template=True,
twitter_creator="@author",
omit_schema=True,
)
```
--------------------------------
### Install seoslug with Npm
Source: https://seoslug.emiliano-go.com/integrations/strapi
Install the seoslug Node.js package using npm for runtime dependency installation.
```bash
RUN npm install seoslug
```
--------------------------------
### Install and Run Mypy for Type Checking
Source: https://seoslug.emiliano-go.com/contributing
Install the mypy type checker and run it on the seoslug source code to verify type hints.
```bash
pip install mypy
mypy src/seoslug/
```
--------------------------------
### Astro CI/CD Pipeline Setup
Source: https://seoslug.emiliano-go.com/integrations/astro
Integrate SEO slug generation into your CI/CD workflow before the Astro build. Ensure Python is set up, dependencies are installed, and the generation script is executed.
```yaml
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- run: pip install "seoslug>=2.0.1" pyyaml
- run: python scripts/generate_seo.py
- run: npm ci
- run: npm run build
```
--------------------------------
### Build Zensical Documentation
Source: https://seoslug.emiliano-go.com/integrations/zensical
Install necessary packages and run the `zensical build` command. The seoslug extension integrates seamlessly without requiring pre-build steps or modifications to CI commands.
```bash
# .github/workflows/deploy-docs.yml
- run: pip install "seoslug>=2.0.1" zensical
- run: zensical build --clean
```
--------------------------------
### Install seoslug with Pip
Source: https://seoslug.emiliano-go.com/integrations/strapi
Install the seoslug Python package using pip for Dockerfile or CI environments.
```bash
# Dockerfile or CI
RUN pip install "seoslug>=2.0.1"
```
--------------------------------
### Starlette App Setup with Routes
Source: https://seoslug.emiliano-go.com/integrations/starlette
Configure your Starlette application by defining routes for different endpoints.
```python
from starlette.applications import Starlette
from starlette.routing import Route
routes = [
Route("/", home),
Route("/{slug}", post_detail),
]
app = Starlette(routes=routes)
```
--------------------------------
### Update CI Workflow for seoslug and Quartz Build
Source: https://seoslug.emiliano-go.com/integrations/quartz
Configure your CI workflow to set up Python, install seoslug and pyyaml, run the seoslug pre-build script, and then build the Quartz site.
```yaml
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- run: pip install "seoslug>=2.0.1" pyyaml
- run: python scripts/generate_seo.py
- run: npm ci
- run: npm run build
```
--------------------------------
### HTML Template Rendering for Multi-Language
Source: https://seoslug.emiliano-go.com/recipes/multi-language
Example of an HTML
section demonstrating how to render canonical URLs, alternate hreflang links, and Open Graph locale tags for a multi-language site.
```html
{{ en_payload.title }}
```
--------------------------------
### Add SEO Slug Generation to Gatsby CI/CD
Source: https://seoslug.emiliano-go.com/integrations/gatsby
This snippet shows how to add a step to install Python dependencies, run a Python script for SEO slug generation, and then proceed with the standard npm installation and Gatsby build. Ensure 'seoslug' and 'pyyaml' are installed and the 'generate_seo.py' script exists.
```yaml
- uses: actions/setup-python@v5
with:
python-version: "3.x"
- run: pip install "seoslug>=2.0.1" pyyaml
- run: python scripts/generate_seo.py
- run: npm ci
- run: npm run build
```
--------------------------------
### Frontmatter with SEO HTML
Source: https://seoslug.emiliano-go.com/integrations/zensical
Example of a Markdown file's frontmatter after the seoslug script has processed it, showing the added 'seo_html' key.
```markdown
---
title: Getting Started
description: How to install and configure seoslug.
seo_html: "Getting Started - Your Site\n"
---
```
--------------------------------
### Generated SEO Payload Example
Source: https://seoslug.emiliano-go.com/recipes/blog-post
An example of the resulting JSON payload after generating SEO data, including title, description, canonical URL, OG data, Twitter card data, and structured schema.jsonld for both BlogPosting and BreadcrumbList.
```json
{
"title": "Hello World - My Blog",
"description": "A brief introduction to the blog.",
"canonical": "https://blog.example.com/blog/hello-world",
"robots": "index,follow",
"og": {
"type": "article",
"title": "Hello World - My Blog",
"description": "A brief introduction to the blog.",
"url": "https://blog.example.com/blog/hello-world",
"image": "https://cdn.example.com/hello.jpg",
"image:width": 1200,
"image:height": 630,
"image:alt": "Hello World featured image",
"site_name": "My Blog",
},
"twitter": {
"card": "summary_large_image",
"title": "Hello World - My Blog",
"description": "A brief introduction to the blog.",
"image": "https://cdn.example.com/hello.jpg",
"image:alt": "Hello World featured image",
},
"schema_jsonld": [
{
"@context": "https://schema.org",
"@type": "BlogPosting",
"headline": "Hello World - My Blog",
"url": "https://blog.example.com/blog/hello-world",
"mainEntityOfPage": {"@id": "https://blog.example.com/blog/hello-world"},
"description": "A brief introduction to the blog.",
"image": "https://cdn.example.com/hello.jpg",
"datePublished": "2025-01-15",
"dateModified": "2025-02-01",
"author": {"@type": "Person", "name": "Jane Doe"},
"publisher": {
"@type": "Organization",
"name": "My Company",
},
},
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{"@type": "ListItem", "position": 1, "name": "Home", "item": "https://blog.example.com/"},
{"@type": "ListItem", "position": 2, "name": "Blog", "item": "https://blog.example.com/blog"},
],
},
],
}
```
--------------------------------
### Updating seoslug Dependencies for Optional Extras
Source: https://seoslug.emiliano-go.com/migration
In 2.0.0, lxml and detrack are optional extras. Update your pip install command to include '[fast]' for lxml+detrack (recommended) or '[light]' for pure-Python fallbacks.
```bash
# Old
pip install seoslug
# New - same behavior as 1.x (recommended)
pip install "seoslug[fast]"
# New - pure Python only (minimal)
pip install "seoslug[light]"
```
--------------------------------
### SEO Configuration Setup
Source: https://seoslug.emiliano-go.com/recipes/blog-post
Configures SEO settings including canonical host, public base URL, URL policies, title template, site name, publisher details, default OG image, schema type mapping, and the custom schema registry.
```python
config = SEOConfig(
canonical_host="blog.example.com",
public_base_url="https://blog.example.com",
url_policy=URLPolicy(
strip_tracking_params=True,
lowercase_paths=True,
trailing_slash="never",
),
title_template="{title} - My Blog",
site_name="My Blog",
publisher_name="My Company",
default_og_image="https://cdn.example.com/default.jpg",
schema_type_map={"post": "BlogPosting"},
schema_registry=registry,
)
```
--------------------------------
### MkDocs Plugin Configuration
Source: https://seoslug.emiliano-go.com/integrations/ssg
Integrate seoslug with MkDocs to generate SEO metadata for pages. This example sets up the SEOConfig and the on_page_markdown hook.
```python
from seoslug import SEOConfig, URLPolicy, SEOEntity, build_seo_payload
config = SEOConfig(
canonical_host="docs.example.com",
public_base_url="https://docs.example.com",
url_policy=URLPolicy(),
)
def on_page_markdown(markdown, page, config, files):
entity = SEOEntity(
entity_type="page",
title=page.title,
status="published",
)
page.seo_payload = build_seo_payload(entity, page.url, config)
```
--------------------------------
### CI Workflow for Seoslug and Zensical Build
Source: https://seoslug.emiliano-go.com/integrations/zensical
This snippet shows how to update a CI workflow to install seoslug and zensical, run the SEO generation script, and then build the Zensical site. This ensures SEO frontmatter is generated before the site is built.
```yaml
- run: pip install "seoslug>=2.0.1" zensical
- run: python scripts/generate_seo.py
- run: zensical build --clean
```
--------------------------------
### Use Seoslug Plugin Service in Controller
Source: https://seoslug.emiliano-go.com/integrations/strapi
This example demonstrates how to use the custom seoslug plugin service within a Strapi controller. It fetches entities, enriches them with SEO data using `buildSeo`, and returns the combined data, providing structured SEO information alongside content.
```javascript
module.exports = ({ strapi }) => ({
async find(ctx) {
const entities = await strapi.entityService.findMany(ctx.query);
const route = ctx.request.path;
const enriched = entities.map((entity) => {
const seo = strapi
.plugin("seoslug")
.service("seoslugService")
.buildSeo(entity, route);
return { ...entity, seo };
});
return enriched;
},
});
```
--------------------------------
### Render SEO Tags with next/head
Source: https://seoslug.emiliano-go.com/integrations/nextjs
Use `next/head` in your layout or page component to render SEO tags dynamically based on provided `seo` data. This example includes handling default tags if `seo` data is not available.
```javascript
import Head from "next/head";
export default function Page({ seo, children }) {
return (
<>
{seo ? (
<>
{seo.title}
{seo.schema_jsonld && (
)}
>
) : (
<>
Default title
>
)}
{children}
>
);
}
```
--------------------------------
### Flask Post Detail Route with Seoslug
Source: https://seoslug.emiliano-go.com/integrations/flask
Integrate seoslug into a Flask route for displaying individual posts. This example includes fetching post data by slug, handling 404 errors, creating a detailed SEOEntity with breadcrumbs, and building the SEO payload.
```python
from flask import Flask, render_template, abort
from seoslug import SEOConfig, URLPolicy, SEOEntity, Breadcrumb, build_seo_payload
app = Flask(__name__)
SEO_CONFIG = SEOConfig(
canonical_host="blog.example.com",
public_base_url="https://blog.example.com",
url_policy=URLPolicy(),
)
@app.route("//")
def post_detail(slug):
post = get_post_by_slug(slug)
if post is None:
abort(404)
entity = SEOEntity(
entity_type="post",
title=post.title,
excerpt=post.excerpt,
status="published",
published_at=post.published_at.isoformat(),
updated_at=post.updated_at.isoformat() if post.updated_at else None,
author_name=post.author,
featured_image=post.cover_url,
breadcrumbs=[
Breadcrumb(name="Blog", url="/"),
Breadcrumb(name=post.title, url=f"/{slug}/"),
],
)
payload = build_seo_payload(entity, f"/{slug}/", SEO_CONFIG)
return render_template("post.html", post=post, seo=payload)
```
--------------------------------
### Starlette Endpoint with ETag Caching
Source: https://seoslug.emiliano-go.com/integrations/starlette
Implement ETag caching in a Starlette endpoint to handle conditional GET requests. This example fetches a post, builds its SEO payload, generates an ETag, and returns a 304 Not Modified response if the ETag matches the request header.
```python
from starlette.responses import Response
async def post_detail(request):
slug = request.path_params["slug"]
post = get_post_by_slug(slug)
entity = SEOEntity(
entity_type="post",
title=post.title,
excerpt=post.excerpt,
status="published",
)
payload = await build_seo_payload_async(entity, f"/{slug}/", SEO_CONFIG)
etag = payload.etag()
if request.headers.get("if-none-match") == etag:
return Response(status_code=304)
html = render_jinja_template("post.html", post=post, seo=payload)
return Response(html, media_type="text/html", headers={"ETag": etag})
```
--------------------------------
### Initialize and Build with QuartzBuilder
Source: https://seoslug.emiliano-go.com/integrations/quartz
Use the QuartzBuilder from seoslug.contrib.quartz to scan content, parse frontmatter, build SEO payloads, and inject metadata into Quartz projects. This is the recommended approach for seamless integration.
```python
from seoslug.contrib.quartz import QuartzBuilder
builder = QuartzBuilder(
content_dir="content",
site_url="https://yoursite.com",
site_name="Your Site",
)
builder.build()
```
--------------------------------
### Initialize and Build with HugoBuilder
Source: https://seoslug.emiliano-go.com/integrations/hugo
Use HugoBuilder to scan content, parse frontmatter, build SEO payloads, and inject metadata. Recommended for most use cases.
```python
from seoslug.contrib.hugo import HugoBuilder
builder = HugoBuilder(
content_dir="content",
site_url="https://yoursite.com",
site_name="Your Site",
)
builder.build()
```
--------------------------------
### Configure Product Price and Offer Details
Source: https://seoslug.emiliano-go.com/configuration/seo-entity
Set the price, currency, and availability for a product entity. These are used to build the `offers` object in the JSON-LD schema.
```python
SEOEntity(
entity_type="product",
price="29.99",
price_currency="USD",
availability="InStock",
)
```
--------------------------------
### Build SEO Payload with Configuration
Source: https://seoslug.emiliano-go.com/
Configure SEO settings and build a payload for a given entity and path. Ensure the SEOConfig and SEOEntity are correctly defined.
```python
from seoslug import SEOConfig, URLPolicy, SEOEntity, build_seo_payload
config = SEOConfig(
canonical_host="blog.example.com",
public_base_url="https://blog.example.com",
url_policy=URLPolicy(
enforce_https=True,
lowercase_paths=True,
trailing_slash="never",
),
)
entity = SEOEntity(
entity_type="post",
slug="introducing-v2",
title="Introducing v2",
excerpt="A fully deterministic SEO payload library.",
)
payload = build_seo_payload(entity, "/blog/introducing-v2", config)
```
--------------------------------
### Create Product SEO Entity
Source: https://seoslug.emiliano-go.com/api-reference
Use `from_product` to create an SEOEntity for a product. Sets `entity_type="product"` and `status="published"`. The price can be a string or a float, and currency defaults to USD.
```python
from seoslug import from_product
entity = from_product(
name="Awesome T-Shirt",
sku="TS-RED-L",
price="29.99",
currency="EUR",
availability="InStock",
description="A comfortable and stylish t-shirt.",
breadcrumbs=[{"name": "Apparel", "url": "/apparel"}]
)
```
--------------------------------
### Strapi Enriched Response Example
Source: https://seoslug.emiliano-go.com/integrations/strapi
Example of an API response from Strapi after seoslug has enriched the entity with SEO data. This includes various SEO-related fields like canonical URLs, Open Graph tags, and Twitter card information.
```json
{
"id": 1,
"attributes": {
"title": "Hello World",
"seoTitle": "Hello World",
"seoDescription": "A brief introduction",
"seo": {
"title": "Hello World | Your Site",
"canonical": "https://yoursite.com/api/posts/1",
"robots": "index,follow",
"og": {
"type": "website",
"title": "Hello World | Your Site",
"description": "A brief introduction",
"url": "https://yoursite.com/api/posts/1",
"image": "https://yoursite.com/images/default-og.png",
"site_name": "Your Site",
"locale": "en_US"
},
"twitter": {
"card": "summary_large_image",
"title": "Hello World | Your Site",
"description": "A brief introduction",
"image": "https://yoursite.com/images/default-og.png"
},
"schema_jsonld": {
"@context": "https://schema.org",
"@type": "WebPage",
"name": "Hello World | Your Site",
"url": "https://yoursite.com/api/posts/1",
"description": "A brief introduction",
"publisher": { "@type": "Organization", "name": "Your Name" }
}
}
}
}
```
--------------------------------
### Basic seoslug Integration in Python
Source: https://seoslug.emiliano-go.com/integrations
Use this pattern for any Python environment. Import necessary classes and functions, configure seoslug, create an entity, and build the payload. Use `payload.to_dict()` for serialization or `build_seo_payload_dict()` as a shorthand.
```python
from seoslug import SEOConfig, SEOEntity, build_seo_payload
config = SEOConfig(
canonical_host="yoursite.com",
public_base_url="https://yoursite.com",
url_policy=...,
)
entity = SEOEntity(
entity_type="page",
title="Hello",
status="published",
)
payload = build_seo_payload(entity, "/hello", config)
```
--------------------------------
### Generated BreadcrumbList Schema
Source: https://seoslug.emiliano-go.com/schema-jsonld
Example of the JSON-LD BreadcrumbList schema automatically generated by seoslug based on the provided entity breadcrumbs.
```json
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "https://blog.example.com/"
},
{
"@type": "ListItem",
"position": 2,
"name": "Blog",
"item": "https://blog.example.com/blog"
},
],
}
```
--------------------------------
### Create a Product SEO Entity with Structured Image
Source: https://seoslug.emiliano-go.com/configuration/seo-entity
Instantiate an SEOEntity for a product, including structured data for the featured image using the OGImage class, along with product-specific fields like SKU, price, currency, and availability.
```python
from seoslug import SEOEntity, OGImage
entity = SEOEntity(
entity_type="product",
title="Premium Widget",
sku="PW-001",
price="49.99",
price_currency="USD",
availability="InStock",
featured_image=OGImage(
url="https://cdn.example.com/widget.jpg",
width=1200,
height=1200,
alt="Premium Widget",
),
)
```
--------------------------------
### SEOConfig Title Template
Source: https://seoslug.emiliano-go.com/seo-payload
Example of configuring a title template for SEO. The template is applied after the title is resolved through its fallback chain.
```python
config = SEOConfig(
...,
title_template="{title} - My Blog",
)
```
--------------------------------
### Build Docker image for Hugo blog
Source: https://seoslug.emiliano-go.com/integrations/ssg
Command to build the Docker image for the Hugo blog using the multi-stage Dockerfile.
```bash
docker build -t my-blog .
```
--------------------------------
### Configure SEO Settings
Source: https://seoslug.emiliano-go.com/recipes/category-page
Set up SEO configuration including canonical host, public base URL, URL policies, title template, site name, default OG image, and schema type mapping.
```python
from seoslug import SEOConfig, URLPolicy
config = SEOConfig(
canonical_host="blog.example.com",
public_base_url="https://blog.example.com",
url_policy=URLPolicy(
lowercase_paths=True,
trailing_slash="never",
strip_tracking_params=True,
),
title_template="{title} - Category - My Blog",
site_name="My Blog",
default_og_image="https://cdn.example.com/category-default.jpg",
schema_type_map={"taxonomy": "CollectionPage"},
)
```
--------------------------------
### Override Schema Type Map
Source: https://seoslug.emiliano-go.com/configuration/seo-config
Customize schema generation by overriding default type mappings. Products are explicitly disabled in this example.
```python
SEOConfig(
...,
schema_type_map={
"post": "BlogPosting",
"product": None, # disable schema for products
},
)
```
--------------------------------
### Configure SEO Settings
Source: https://seoslug.emiliano-go.com/recipes/product-page
Set up the base configuration for SEO, including canonical host, public URL, URL policies, title templates, and default OG image. This configuration is essential for generating consistent SEO metadata.
```python
from seoslug import SEOConfig, URLPolicy
config = SEOConfig(
canonical_host="shop.example.com",
public_base_url="https://shop.example.com",
url_policy=URLPolicy(
lowercase_paths=True,
trailing_slash="never",
strip_tracking_params=True,
),
title_template="{title} - My Shop",
site_name="My Shop",
default_og_image="https://cdn.example.com/default-product.jpg",
schema_type_map={"product": "Product"},
)
```
--------------------------------
### Example SEO Payload Result
Source: https://seoslug.emiliano-go.com/recipes/category-page
The generated payload includes metadata for title, description, canonical URL, robots, Open Graph, Twitter cards, and schema.jsonld.
```json
{
"title": "Python Tutorials - Category - My Blog",
"description": "A collection of Python tutorials for beginners and advanced users.",
"canonical": "https://blog.example.com/topics/python",
"robots": "index,follow",
"og": {
"type": "website",
"title": "Python Tutorials - Category - My Blog",
"description": "A collection of Python tutorials for beginners and advanced users.",
"url": "https://blog.example.com/topics/python",
"image": "https://cdn.example.com/category-default.jpg",
"site_name": "My Blog",
},
"twitter": {
"card": "summary_large_image",
"title": "Python Tutorials - Category - My Blog",
"description": "A collection of Python tutorials for beginners and advanced users.",
"image": "https://cdn.example.com/category-default.jpg",
},
"schema_jsonld": {
"@context": "https://schema.org",
"@type": "CollectionPage",
"name": "Python Tutorials - Category - My Blog",
"url": "https://blog.example.com/topics/python",
"description": "A collection of Python tutorials for beginners and advanced users.",
"image": "https://cdn.example.com/category-default.jpg",
},
}
```
--------------------------------
### Basic Flask Route with Seoslug
Source: https://seoslug.emiliano-go.com/integrations/flask
Use this snippet for a basic Flask route, such as a homepage. It initializes the Flask app, defines SEO configuration, creates an SEOEntity, builds the SEO payload, and renders a template.
```python
from flask import Flask, render_template, request
from seoslug import SEOConfig, URLPolicy, SEOEntity, build_seo_payload
app = Flask(__name__)
SEO_CONFIG = SEOConfig(
canonical_host="blog.example.com",
public_base_url="https://blog.example.com",
url_policy=URLPolicy(),
)
@app.route("/")
def home():
entity = SEOEntity(
entity_type="home",
title="My Blog",
excerpt="A blog about things.",
status="published",
)
payload = build_seo_payload(entity, "/", SEO_CONFIG)
return render_template("home.html", seo=payload)
```
--------------------------------
### Get Registered Hooks
Source: https://seoslug.emiliano-go.com/api-reference
Use get_registered_hooks to retrieve a copy of all currently registered hooks, keyed by their hook point name. This is helpful for inspection and testing purposes.
```python
get_registered_hooks() -> dict[str, list[Callable]]
```
--------------------------------
### URLPolicyError Exception
Source: https://seoslug.emiliano-go.com/api-reference
Raised when a URLPolicy field fails validation or a path argument is not a string. Examples include invalid trailing slash settings or non-string path inputs.
```python
seoslug.exceptions.URLPolicyError
```
--------------------------------
### normalize_path
Source: https://seoslug.emiliano-go.com/api-reference
Normalizes a URL path without host or scheme, applying lowercase, trailing slash, and duplicate-slash rules. The function always returns a path that starts with '/'.
```APIDOC
## normalize_path
### Description
Normalizes a URL path without host or scheme, applying lowercase, trailing slash, and duplicate-slash rules. The function always returns a path that starts with '/'.
### Method
`def normalize_path(
path: str,
policy: URLPolicy,
) -> str`
### Parameters
#### Path Parameters
None
#### Query Parameters
None
#### Request Body
None
### Request Example
```python
normalize_path("/Blog//Post", URLPolicy())
# "/blog/post"
```
### Response
#### Success Response (string)
Returns a normalized URL path string.
#### Response Example
None provided.
### Error Handling
Raises `URLPolicyError` for non-string input.
```
--------------------------------
### Generic Schema.org JSON-LD Output
Source: https://seoslug.emiliano-go.com/schema-jsonld
Example of the JSON-LD structure generated by the generic builder, including common fields like context, type, name, URL, and publication dates.
```json
{
"@context": "https://schema.org",
"@type": "Article",
"name": "My Post",
"url": "https://blog.example.com/posts/my-post",
"description": "A brief description",
"image": "https://cdn.example.com/image.jpg",
"datePublished": "2025-01-15",
"dateModified": "2025-02-01",
"mainEntityOfPage": {
"@id": "https://blog.example.com/posts/my-post"
},
"author": {
"@type": "Person",
"name": "Jane Doe"
},
"publisher": {
"@type": "Organization",
"name": "My Company",
"logo": "https://cdn.example.com/logo.png"
},
}
```
--------------------------------
### Product Entity Builder
Source: https://seoslug.emiliano-go.com/schema-jsonld
Constructs a Schema.org Product JSON-LD. Use this when defining items for sale. It includes fields like SKU, price, and availability.
```python
entity = SEOEntity(
entity_type="product",
title="Wireless Headphones",
sku="WH-1000",
price="79.99",
price_currency="USD",
availability="InStock",
)
```
--------------------------------
### Initialize SEOConfig with Canonical Host
Source: https://seoslug.emiliano-go.com/configuration/seo-config
Set the canonical host for all generated URLs. This should be a bare hostname without scheme or path.
```python
SEOConfig(
canonical_host="blog.example.com",
public_base_url="https://blog.example.com",
url_policy=URLPolicy(),
)
```
--------------------------------
### Configure Global SEO Settings
Source: https://seoslug.emiliano-go.com/configuration
Set up global SEO configurations including canonical host, public base URL, and URL policies. This is used for site-wide SEO defaults.
```python
from seoslug import SEOConfig, URLPolicy
config = SEOConfig(
canonical_host="blog.example.com",
public_base_url="https://blog.example.com",
url_policy=URLPolicy(),
)
```
--------------------------------
### Add seoslug Build Step to package.json
Source: https://seoslug.emiliano-go.com/integrations/quartz
Integrate the seoslug pre-build script into your package.json scripts for building and serving your Quartz site.
```json
{
"scripts": {
"build:seo": "python scripts/generate_seo.py",
"build": "npm run build:seo && quartz build",
"serve": "npm run build:seo && quartz build --serve"
}
}
```
--------------------------------
### Update CI Workflow for seoslug Integration
Source: https://seoslug.emiliano-go.com/integrations/hugo
This GitHub Actions workflow installs seoslug and PyYAML, runs a Python script to generate SEO data, builds the Hugo site, and deploys it.
```yaml
- uses: actions/setup-python@v5
with:
python-version: '3.x'
- run: pip install "seoslug>=2.0.1" pyyaml
- run: python scripts/generate_seo.py
- uses: peaceiris/actions-hugo@v3
with:
hugo-version: 'latest'
- run: hugo --minify
- uses: peaceiris/actions-gh-pages@v4
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./public
```
--------------------------------
### Generate SEO Manifest Script (Python)
Source: https://seoslug.emiliano-go.com/integrations/nuxt
Create a Python script to walk your content directory, build an SEO payload for each route, and write a JSON manifest to `public/seo-manifest.json`. This is used for static site generation.
```python
"""Pre-build: generates SEO JSON manifest for Nuxt."""
from __future__ import annotations
import json
import re
import sys
from pathlib import Path
import yaml
from seoslug import (
SEOConfig,
URLPolicy,
SEOEntity,
OGImage,
Robots,
build_seo_payload,
)
PROJECT_ROOT = Path(__file__).resolve().parent.parent
CONTENT_DIR = PROJECT_ROOT / "content"
SITE_URL = "https://yoursite.com"
SITE_NAME = "Your Site"
SEO_CONFIG = SEOConfig(
canonical_host="yoursite.com",
public_base_url=SITE_URL,
url_policy=URLPolicy(
enforce_https=True,
lowercase_paths=True,
trailing_slash="always",
),
site_name=SITE_NAME,
default_og_image=OGImage(
url=f"{SITE_URL}/images/default-og.png",
width=1200,
height=630,
),
publisher_name="Your Name",
title_template="{title} | Your Site",
default_robots=Robots(index=True, follow=True),
locale="en_US",
twitter_site="@yourhandle",
)
FM_RE = re.compile(
r"^-{3}[ \r\t]*?\n(.*?[\r\n])(?:\.{3}|-{3})[ \r\t]*\n",
re.UNICODE | re.DOTALL,
)
def route_path_from_file(filepath: Path) -> str:
rel = filepath.relative_to(CONTENT_DIR)
parts = list(rel.parts)
if parts[-1] in ("index.md", "_index.md"):
parts.pop()
if not parts:
return "/"
return "/" + "/".join(parts) + "/"
stem = Path(*parts).with_suffix("")
return "/" + str(stem) + "/"
def extract_excerpt(body: str, max_chars: int = 160) -> str:
body = re.sub(r"^#\s+.*\n?", "", body, count=1).strip()
para = re.split(r"\n\s*\n", body, maxsplit=1)[0].strip()
clean = re.sub(r"[`*_\\\[\]()>
|#{}]", "", para)
clean = re.sub(r"\s+", " ", clean).strip()
if not clean:
return ""
if len(clean) <= max_chars:
return clean
break_at = clean.rfind(" ", 0, max_chars)
return clean[:break_at] + "..." if break_at > 0 else clean[:max_chars] + "..."
def main() -> int:
manifest: dict[str, dict] = {}
for md_file in sorted(CONTENT_DIR.rglob("*.md")):
text = md_file.read_text(encoding="utf-8")
meta: dict = {}
body = text
if text.startswith("---"):
m = FM_RE.match(text)
if m:
try:
parsed = yaml.safe_load(m.group(1))
if isinstance(parsed, dict):
meta = parsed
except Exception:
pass
body = text[m.end():].lstrip("\n")
route = route_path_from_file(md_file)
title = meta.get("title") or ""
if not title:
m = re.search(r"^#\s+(.+)", body, re.MULTILINE)
if m:
title = re.sub(r"[`*_]", "", m.group(1).strip())
description = meta.get("description") or extract_excerpt(body)
entity = SEOEntity(
entity_type="page",
title=title or SITE_NAME,
excerpt=description or None,
)
payload = build_seo_payload(entity, route, SEO_CONFIG)
if payload is None:
continue
manifest[route] = payload.to_dict()
output = PROJECT_ROOT / "public" / "seo-manifest.json"
output.write_text(json.dumps(manifest, indent=2, ensure_ascii=False))
print(f"Written {output} ({len(manifest)} routes)")
return 0
if __name__ == "__main__":
sys.exit(main())
```
--------------------------------
### Generated SEO Metadata JSON
Source: https://seoslug.emiliano-go.com/integrations/ssg
Example of the JSON output for a blog post, including canonical URL, description, robots directives, Open Graph, Twitter card, and JSON-LD schema.
```json
{
"canonical": "https://blog.example.com/posts/hello-world/",
"description": "My first blog post about getting started.",
"robots": "index,follow",
"og": {
"title": "Hello World",
"description": "My first blog post about getting started.",
"url": "https://blog.example.com/posts/hello-world/",
"image": "https://blog.example.com/images/default-og.png",
"type": "article"
},
"twitter": {
"card": "summary_large_image",
"title": "Hello World",
"description": "My first blog post about getting started.",
"image": "https://blog.example.com/images/default-og.png"
},
"schema_jsonld": {
"@context": "https://schema.org",
"@type": "Article",
"headline": "Hello World",
"description": "My first blog post about getting started.",
"url": "https://blog.example.com/posts/hello-world/",
"author": {
"@type": "Person",
"name": "Your Name"
},
"publisher": {
"@type": "Organization",
"name": "Dev Blog"
}
}
}
```
--------------------------------
### SEO Configuration
Source: https://seoslug.emiliano-go.com/recipes/multi-language
Initializes the SEOConfig with host, base URL, URL policy, and templating for site-wide SEO settings.
```python
from seoslug import SEOConfig, URLPolicy
config = SEOConfig(
canonical_host="example.com",
public_base_url="https://example.com",
url_policy=URLPolicy(
lowercase_paths=True,
trailing_slash="never",
),
title_template="{title} - My Site",
site_name="My Site",
publisher_name="My Company",
schema_type_map={"post": "Article"},
)
```
--------------------------------
### Call Composable in Nuxt Page
Source: https://seoslug.emiliano-go.com/integrations/nuxt
Demonstrates how to call the `useSeo` composable within a Nuxt page's script setup. It shows both basic usage and how to pass a specific route for dynamic URLs.
```vue
My Page
```
```vue
```
--------------------------------
### FAQPage Entity Builder
Source: https://seoslug.emiliano-go.com/schema-jsonld
Builds Schema.org FAQPage JSON-LD. Use this to structure frequently asked questions and their answers for better search visibility.
```python
from seoslug import FAQItem
entity = SEOEntity(
entity_type="faq",
title="FAQ",
faq_items=[
FAQItem(question="Q1?", answer="A1."),
FAQItem(question="Q2?", answer="A2."),
],
)
```
--------------------------------
### Filter Hidden Files and Directories
Source: https://seoslug.emiliano-go.com/integrations/ssg
This Python code filters out hidden files and directories (those starting with '.') when globbing for Markdown files. This ensures that SEO payloads are not generated for hidden drafts or temporary folders.
```python
md_files = sorted(
f for f in content_dir.rglob("*.md")
if not any(part.startswith(".") for part in f.relative_to(content_dir).parts)
)
```
--------------------------------
### SchemaRegistry Operations: Register, Get, Unregister
Source: https://seoslug.emiliano-go.com/registry
Shows the basic operations for managing generators within the SchemaRegistry: registering a generator function, retrieving it, and unregistering it. Raises ValueError for empty schema type strings.
```python
registry = SchemaRegistry()
# Register
registry.register("Podcast", my_generator)
# Look up
gen = registry.get("Podcast") # returns my_generator or None
# Unregister
registry.unregister("Podcast")
registry.get("Podcast") # None
```
--------------------------------
### Normalize Path
Source: https://seoslug.emiliano-go.com/api-reference
Normalizes a URL path without host or scheme, applying lowercase, trailing slash, and duplicate-slash rules. Always returns a path starting with '/'. Raises `URLPolicyError` for non-string input.
```python
def normalize_path(
path: str,
policy: URLPolicy,
) -> str
```
```python
normalize_path("/Blog//Post", URLPolicy())
# "/blog/post"
```
--------------------------------
### Example SEO Payload Result
Source: https://seoslug.emiliano-go.com/recipes/product-page
The generated SEO payload includes metadata for title, description, canonical URL, robots directives, Open Graph tags, Twitter card tags, and structured JSON-LD for schema.org.
```json
{
"title": "Wireless Headphones Pro - My Shop",
"description": "Premium wireless headphones with active noise cancellation.",
"canonical": "https://shop.example.com/products/headphones-pro",
"robots": "index,follow",
"og": {
"type": "website",
"title": "Wireless Headphones Pro - My Shop",
"description": "Premium wireless headphones with active noise cancellation.",
"url": "https://shop.example.com/products/headphones-pro",
"image": "https://cdn.example.com/headphones.jpg",
"image:width": 1200,
"image:height": 1200,
"image:alt": "Wireless Headphones Pro",
"site_name": "My Shop",
},
"twitter": {
"card": "summary_large_image",
"title": "Wireless Headphones Pro - My Shop",
"description": "Premium wireless headphones with active noise cancellation.",
"image": "https://cdn.example.com/headphones.jpg",
"image:alt": "Wireless Headphones Pro",
},
"schema_jsonld": [
{
"@context": "https://schema.org",
"@type": "Product",
"name": "Wireless Headphones Pro - My Shop",
"url": "https://shop.example.com/products/headphones-pro",
"description": "Premium wireless headphones with active noise cancellation.",
"image": "https://cdn.example.com/headphones.jpg",
"sku": "WH-PRO-001",
"offers": {
"@type": "Offer",
"price": "79.99",
"priceCurrency": "USD",
"availability": "https://schema.org/InStock",
},
},
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{"@type": "ListItem", "position": 1, "name": "Home", "item": "https://shop.example.com/"},
{"@type": "ListItem", "position": 2, "name": "Audio", "item": "https://shop.example.com/audio"},
{"@type": "ListItem", "position": 3, "name": "Headphones", "item": "https://shop.example.com/audio/headphones"},
],
},
],
}
```
--------------------------------
### Configure SEO with Sub-Path Deployment
Source: https://seoslug.emiliano-go.com/configuration/seo-config
Set the public_base_url to include the full path when your site runs under a path prefix. This ensures all canonical URLs are correctly prepended.
```python
SEOConfig(
canonical_host="example.com",
public_base_url="https://example.com/blog/",
url_policy=URLPolicy(),
)
```
--------------------------------
### Async seoslug Integration
Source: https://seoslug.emiliano-go.com/integrations
For asynchronous frameworks, import the `build_seo_payload_async` function from `seoslug.async_builder`.
```python
from seoslug.async_builder import build_seo_payload_async
```
--------------------------------
### Initialize SEOConfig with URL Policy
Source: https://seoslug.emiliano-go.com/configuration/seo-config
Specify URL normalization rules using a URLPolicy instance. This controls how URLs are processed and formatted.
```python
SEOConfig(
canonical_host="blog.example.com",
public_base_url="https://blog.example.com",
url_policy=URLPolicy(trailing_slash="always"),
)
```
--------------------------------
### Generate Metadata in Next.js App Router
Source: https://seoslug.emiliano-go.com/integrations/nextjs
Use the `generateMetadata` function in Next.js App Router to dynamically set metadata for each route. This example shows how to fetch or define SEO entities and build the SEO payload using `seoslug`.
```javascript
import type { Metadata } from "next";
import { SEOConfig, SEOEntity, build_seo_payload } from "seoslug";
const SEO_CONFIG = new SEOConfig({
canonical_host: "yoursite.com",
public_base_url: "https://yoursite.com/",
site_name: "Your Site",
title_template: "{title} | Your Site",
});
export async function generateMetadata({ params }): Promise {
const entity = new SEOEntity({
entity_type: "page",
title: "Page Title",
excerpt: "Page description...",
});
const payload = build_seo_payload(entity, `/${params.slug}/`, SEO_CONFIG);
if (!payload) return {};
return {
title: payload.title,
description: payload.description,
robots: payload.robots,
openGraph: payload.og,
twitter: payload.twitter,
other: {
"application/ld+json": JSON.stringify(payload.schema_jsonld),
},
};
}
```
--------------------------------
### Custom Podcast Schema Generation with Detailed Fields
Source: https://seoslug.emiliano-go.com/registry
An advanced example of a custom podcast generator that includes conditional logic for adding description, image, author, and sameAs fields based on the entity and provided data. It demonstrates how to build a more complete schema.
```python
from seoslug import SchemaRegistry, SEOConfig, URLPolicy, SEOEntity, build_seo_payload
registry = SchemaRegistry()
def podcast_gen(entity, config, canonical, title, description, og_image):
schema = {
"@context": "https://schema.org",
"@type": "Podcast",
"name": title,
"url": canonical,
}
if description:
schema["description"] = description
if og_image:
schema["image"] = og_image
if entity.author_name:
schema["author"] = {"@type": "Person", "name": entity.author_name}
if entity.same_as:
schema["sameAs"] = entity.same_as
return schema
registry.register("Podcast", podcast_gen)
config = SEOConfig(
canonical_host="podcast.example.com",
public_base_url="https://podcast.example.com",
url_policy=URLPolicy(),
schema_type_map={"post": "Podcast"},
schema_registry=registry,
)
entity = SEOEntity(
entity_type="post",
title="Episode 42: The Future of AI",
author_name="Jane Doe",
same_as=["https://twitter.com/mypodcast"],
)
payload = build_seo_payload(entity, "/episodes/42", config)
# payload["schema_jsonld"]["@type"] == "Podcast"
```
--------------------------------
### Build an SEO Entity
Source: https://seoslug.emiliano-go.com/api-reference
Use the fluent builder to construct an SEOEntity object. Ensure all required fields like entity_type are set before building.
```python
entity = (
SEOEntityBuilder()
.entity_type("product")
.title("Widget")
.sku("W-001")
.price(29.99)
.availability("InStock")
.build()
)
```