Skip to content

SEO API Reference

This section provides API-level reference for FastStrap SEO helpers.

SEO Meta Tags

faststrap.seo.meta.SEO(title=None, description=None, keywords=None, image=None, url=None, og_type='website', article=False, published_time=None, modified_time=None, author=None, section=None, tags=None, twitter_card='summary_large_image', twitter_site=None, twitter_creator=None, robots='index, follow', canonical=None, locale=None, alternate_locales=None, **kwargs)

Generate comprehensive SEO meta tags.

Creates meta tags for basic SEO, Open Graph, and Twitter Cards.

Parameters:

Name Type Description Default
title str | None

Page title (also used for og:title and twitter:title)

None
description str | None

Page description (also used for og:description)

None
keywords list[str] | None

List of keywords for meta keywords tag

None
image str | None

Image URL for social sharing (og:image, twitter:image)

None
url str | None

Canonical URL for the page (og:url)

None
og_type Literal['website', 'article', 'product']

Open Graph type (website, article, product)

'website'
article bool

If True, adds article-specific meta tags

False
published_time str | None

Article published time (ISO 8601 format)

None
modified_time str | None

Article modified time (ISO 8601 format)

None
author str | None

Article author name

None
section str | None

Article section/category

None
tags list[str] | None

Article tags

None
twitter_card Literal['summary', 'summary_large_image', 'app', 'player']

Twitter card type

'summary_large_image'
twitter_site str | None

Twitter site handle (@username)

None
twitter_creator str | None

Twitter creator handle (@username)

None
robots str

Robots meta tag value

'index, follow'
canonical str | None

Canonical URL (if different from url)

None
locale str | None

Page locale (e.g., "en_US")

None
alternate_locales list[str] | None

List of alternate locales

None
**kwargs Any

Additional meta tags as key-value pairs

{}

Returns:

Type Description
tuple[Any, ...]

Tuple of meta tag elements to include in page head

Example

Basic usage:

SEO( ... title="My Page - Site Name", ... description="Page description", ... image="/assets/og-image.jpg" ... )

Article page:

SEO( ... title="Blog Post Title", ... description="Post excerpt", ... image="/assets/post-image.jpg", ... article=True, ... published_time="2026-02-12T10:00:00Z", ... author="John Doe", ... tags=["python", "fasthtml"] ... )

Note
  • Title, description, and image are used for both basic meta tags and social sharing
  • If article=True, type is automatically set to "article"
  • Canonical URL defaults to url parameter if not explicitly set
Source code in src/faststrap/seo/meta.py
def SEO(
    title: str | None = None,
    description: str | None = None,
    keywords: list[str] | None = None,
    image: str | None = None,
    url: str | None = None,
    og_type: Literal["website", "article", "product"] = "website",
    # Article-specific
    article: bool = False,
    published_time: str | None = None,
    modified_time: str | None = None,
    author: str | None = None,
    section: str | None = None,
    tags: list[str] | None = None,
    # Twitter-specific
    twitter_card: Literal[
        "summary", "summary_large_image", "app", "player"
    ] = "summary_large_image",
    twitter_site: str | None = None,
    twitter_creator: str | None = None,
    # Advanced
    robots: str = "index, follow",
    canonical: str | None = None,
    locale: str | None = None,
    alternate_locales: list[str] | None = None,
    **kwargs: Any,
) -> tuple[Any, ...]:
    """Generate comprehensive SEO meta tags.

    Creates meta tags for basic SEO, Open Graph, and Twitter Cards.

    Args:
        title: Page title (also used for og:title and twitter:title)
        description: Page description (also used for og:description)
        keywords: List of keywords for meta keywords tag
        image: Image URL for social sharing (og:image, twitter:image)
        url: Canonical URL for the page (og:url)
        og_type: Open Graph type (website, article, product)
        article: If True, adds article-specific meta tags
        published_time: Article published time (ISO 8601 format)
        modified_time: Article modified time (ISO 8601 format)
        author: Article author name
        section: Article section/category
        tags: Article tags
        twitter_card: Twitter card type
        twitter_site: Twitter site handle (@username)
        twitter_creator: Twitter creator handle (@username)
        robots: Robots meta tag value
        canonical: Canonical URL (if different from url)
        locale: Page locale (e.g., "en_US")
        alternate_locales: List of alternate locales
        **kwargs: Additional meta tags as key-value pairs

    Returns:
        Tuple of meta tag elements to include in page head

    Example:
        Basic usage:
        >>> SEO(
        ...     title="My Page - Site Name",
        ...     description="Page description",
        ...     image="/assets/og-image.jpg"
        ... )

        Article page:
        >>> SEO(
        ...     title="Blog Post Title",
        ...     description="Post excerpt",
        ...     image="/assets/post-image.jpg",
        ...     article=True,
        ...     published_time="2026-02-12T10:00:00Z",
        ...     author="John Doe",
        ...     tags=["python", "fasthtml"]
        ... )

    Note:
        - Title, description, and image are used for both basic meta tags and social sharing
        - If article=True, type is automatically set to "article"
        - Canonical URL defaults to url parameter if not explicitly set
    """
    elements: list[Any] = []

    legacy_type = kwargs.pop("type", None)
    if legacy_type is not None:
        warnings.warn(
            "SEO(type=...) is deprecated; use SEO(og_type=...) instead.",
            DeprecationWarning,
            stacklevel=2,
        )
        if og_type == "website":
            og_type = legacy_type

    # Basic meta tags
    if title:
        elements.append(Title(title))

    if description:
        elements.append(Meta(name="description", content=description))

    if keywords:
        elements.append(Meta(name="keywords", content=", ".join(keywords)))

    if robots:
        elements.append(Meta(name="robots", content=robots))

    # Canonical URL
    canonical_url = canonical or url
    if canonical_url:
        elements.append(Link(rel="canonical", href=canonical_url))

    # Open Graph tags
    has_og_content = any(
        [
            title,
            description,
            image,
            url,
            locale,
            alternate_locales,
            article,
            published_time,
            modified_time,
            author,
            section,
            tags,
        ]
    )

    if title:
        elements.append(Meta(property="og:title", content=title))

    if description:
        elements.append(Meta(property="og:description", content=description))

    if image:
        elements.append(Meta(property="og:image", content=image))

    if url:
        elements.append(Meta(property="og:url", content=url))

    # Set type to article if article=True
    resolved_og_type = "article" if article else og_type
    if has_og_content:
        elements.append(Meta(property="og:type", content=resolved_og_type))

    if locale:
        elements.append(Meta(property="og:locale", content=locale))

    if alternate_locales:
        for alt_locale in alternate_locales:
            elements.append(Meta(property="og:locale:alternate", content=alt_locale))

    # Article-specific tags
    if article or resolved_og_type == "article":
        if published_time:
            elements.append(Meta(property="article:published_time", content=published_time))

        if modified_time:
            elements.append(Meta(property="article:modified_time", content=modified_time))

        if author:
            elements.append(Meta(property="article:author", content=author))

        if section:
            elements.append(Meta(property="article:section", content=section))

        if tags:
            for tag in tags:
                elements.append(Meta(property="article:tag", content=tag))

    # Twitter Card tags
    elements.append(Meta(name="twitter:card", content=twitter_card))

    if title:
        elements.append(Meta(name="twitter:title", content=title))

    if description:
        elements.append(Meta(name="twitter:description", content=description))

    if image:
        elements.append(Meta(name="twitter:image", content=image))

    if twitter_site:
        elements.append(Meta(name="twitter:site", content=twitter_site))

    if twitter_creator:
        elements.append(Meta(name="twitter:creator", content=twitter_creator))

    # Additional custom meta tags
    for key, value in kwargs.items():
        if value is not None:
            # Convert underscores to hyphens for attribute names
            attr_name = key.replace("_", "-")
            elements.append(Meta(name=attr_name, content=str(value)))

    return tuple(elements)

PageMeta Composer

faststrap.seo.page_meta.PageMeta(title=None, description=None, keywords=None, image=None, url=None, canonical=None, type='website', robots='index, follow', twitter_site=None, twitter_creator=None, locale=None, include_pwa=False, pwa_name=None, pwa_short_name=None, pwa_theme_color='#ffffff', pwa_background_color='#ffffff', favicon_url=None, extra_meta=None)

Compose page metadata with deduplication guarantees.

Source code in src/faststrap/seo/page_meta.py
def PageMeta(
    title: str | None = None,
    description: str | None = None,
    keywords: list[str] | None = None,
    image: str | None = None,
    url: str | None = None,
    canonical: str | None = None,
    type: Literal["website", "article", "product"] = "website",
    robots: str = "index, follow",
    twitter_site: str | None = None,
    twitter_creator: str | None = None,
    locale: str | None = None,
    include_pwa: bool = False,
    pwa_name: str | None = None,
    pwa_short_name: str | None = None,
    pwa_theme_color: str = "#ffffff",
    pwa_background_color: str = "#ffffff",
    favicon_url: str | None = None,
    extra_meta: dict[str, Any] | None = None,
) -> tuple[Any, ...]:
    """Compose page metadata with deduplication guarantees."""
    elements: list[Any] = []

    if title is not None:
        elements.append(Title(title))
    elements.extend(
        SEO(
            title=title,
            description=description,
            keywords=keywords,
            image=image,
            url=url,
            canonical=canonical,
            og_type=type,
            robots=robots,
            twitter_site=twitter_site,
            twitter_creator=twitter_creator,
            locale=locale,
        )
    )

    if include_pwa:
        elements.extend(
            PwaMeta(
                name=pwa_name or title or "Faststrap App",
                short_name=pwa_short_name or pwa_name or "Faststrap",
                description=description,
                theme_color=pwa_theme_color,
                background_color=pwa_background_color,
            )
        )

    if favicon_url:
        elements.extend(create_favicon_links(favicon_url))

    if extra_meta:
        for key, value in extra_meta.items():
            if value is None:
                continue
            attr = key.replace("_", "-")
            elements.append(Meta(name=attr, content=str(value)))

    canonical_url = canonical or url
    if canonical_url:
        elements.append(Link(rel="canonical", href=canonical_url))

    return _dedupe(elements)

Structured Data

faststrap.seo.structured_data.StructuredData

Helper class for generating Schema.org structured data (JSON-LD).

Provides static methods for common structured data types including Article, Product, Breadcrumb, Organization, and LocalBusiness.

Example

Article structured data:

StructuredData.article( ... headline="My Blog Post", ... description="Post description", ... image="https://example.com/image.jpg", ... author="John Doe", ... published="2026-02-12T10:00:00Z" ... )

Product structured data:

StructuredData.product( ... name="Amazing Product", ... description="Product description", ... image="https://example.com/product.jpg", ... price="99.99", ... currency="USD", ... rating=4.5, ... review_count=127 ... )

Source code in src/faststrap/seo/structured_data.py
class StructuredData:
    """Helper class for generating Schema.org structured data (JSON-LD).

    Provides static methods for common structured data types including
    Article, Product, Breadcrumb, Organization, and LocalBusiness.

    Example:
        Article structured data:
        >>> StructuredData.article(
        ...     headline="My Blog Post",
        ...     description="Post description",
        ...     image="https://example.com/image.jpg",
        ...     author="John Doe",
        ...     published="2026-02-12T10:00:00Z"
        ... )

        Product structured data:
        >>> StructuredData.product(
        ...     name="Amazing Product",
        ...     description="Product description",
        ...     image="https://example.com/product.jpg",
        ...     price="99.99",
        ...     currency="USD",
        ...     rating=4.5,
        ...     review_count=127
        ... )
    """

    @staticmethod
    def article(
        headline: str,
        description: str,
        image: str,
        author: str,
        published: str,
        modified: str | None = None,
        **kwargs: Any,
    ) -> Script:
        """Generate Article structured data (JSON-LD).

        Args:
            headline: Article headline/title
            description: Article description
            image: Article image URL
            author: Author name
            published: Published date (ISO 8601 format)
            modified: Modified date (ISO 8601 format), optional
            **kwargs: Additional Schema.org Article properties

        Returns:
            Script element with JSON-LD structured data

        Example:
            >>> StructuredData.article(
            ...     headline="10 Tips for Better Python Code",
            ...     description="Learn how to write cleaner Python",
            ...     image="https://example.com/python-tips.jpg",
            ...     author="Jane Smith",
            ...     published="2026-02-12T10:00:00Z",
            ...     modified="2026-02-12T14:30:00Z"
            ... )
        """
        data = {
            "@context": "https://schema.org",
            "@type": "Article",
            "headline": headline,
            "description": description,
            "image": image,
            "author": {"@type": "Person", "name": author},
            "datePublished": published,
        }

        if modified:
            data["dateModified"] = modified

        # Add any additional properties
        data.update(kwargs)

        return Script(
            json.dumps(data, indent=2),
            type="application/ld+json",
        )

    @staticmethod
    def product(
        name: str,
        description: str,
        image: str,
        price: str,
        currency: str = "USD",
        rating: float | None = None,
        review_count: int | None = None,
        availability: str = "InStock",
        **kwargs: Any,
    ) -> Script:
        """Generate Product structured data (JSON-LD).

        Args:
            name: Product name
            description: Product description
            image: Product image URL
            price: Product price (as string)
            currency: Currency code (default: "USD")
            rating: Average rating (0-5)
            review_count: Number of reviews
            availability: Availability status (InStock, OutOfStock, PreOrder)
            **kwargs: Additional Schema.org Product properties

        Returns:
            Script element with JSON-LD structured data

        Example:
            >>> StructuredData.product(
            ...     name="Wireless Headphones",
            ...     description="Premium noise-cancelling headphones",
            ...     image="https://example.com/headphones.jpg",
            ...     price="199.99",
            ...     currency="USD",
            ...     rating=4.5,
            ...     review_count=342,
            ...     availability="InStock"
            ... )
        """
        data: dict[str, Any] = {
            "@context": "https://schema.org",
            "@type": "Product",
            "name": name,
            "description": description,
            "image": image,
            "offers": {
                "@type": "Offer",
                "price": price,
                "priceCurrency": currency,
                "availability": f"https://schema.org/{availability}",
            },
        }

        if rating is not None and review_count is not None:
            data["aggregateRating"] = {
                "@type": "AggregateRating",
                "ratingValue": rating,
                "reviewCount": review_count,
            }

        # Add any additional properties
        data.update(kwargs)

        return Script(
            json.dumps(data, indent=2),
            type="application/ld+json",
        )

    @staticmethod
    def breadcrumb(items: list[tuple[str, str]]) -> Script:
        """Generate BreadcrumbList structured data (JSON-LD).

        Args:
            items: List of (name, url) tuples representing breadcrumb items

        Returns:
            Script element with JSON-LD structured data

        Example:
            >>> StructuredData.breadcrumb([
            ...     ("Home", "https://example.com/"),
            ...     ("Products", "https://example.com/products"),
            ...     ("Laptops", "https://example.com/products/laptops")
            ... ])
        """
        list_items = [
            {
                "@type": "ListItem",
                "position": i + 1,
                "name": name,
                "item": url,
            }
            for i, (name, url) in enumerate(items)
        ]

        data = {
            "@context": "https://schema.org",
            "@type": "BreadcrumbList",
            "itemListElement": list_items,
        }

        return Script(
            json.dumps(data, indent=2),
            type="application/ld+json",
        )

    @staticmethod
    def organization(
        name: str,
        url: str,
        logo: str,
        social_links: list[str] | None = None,
        **kwargs: Any,
    ) -> Script:
        """Generate Organization structured data (JSON-LD).

        Args:
            name: Organization name
            url: Organization website URL
            logo: Organization logo URL
            social_links: List of social media profile URLs
            **kwargs: Additional Schema.org Organization properties

        Returns:
            Script element with JSON-LD structured data

        Example:
            >>> StructuredData.organization(
            ...     name="Acme Corp",
            ...     url="https://acme.com",
            ...     logo="https://acme.com/logo.png",
            ...     social_links=[
            ...         "https://twitter.com/acmecorp",
            ...         "https://linkedin.com/company/acmecorp"
            ...     ]
            ... )
        """
        data: dict[str, Any] = {
            "@context": "https://schema.org",
            "@type": "Organization",
            "name": name,
            "url": url,
            "logo": logo,
        }

        if social_links:
            data["sameAs"] = social_links

        # Add any additional properties
        data.update(kwargs)

        return Script(
            json.dumps(data, indent=2),
            type="application/ld+json",
        )

    @staticmethod
    def local_business(
        name: str,
        address: dict[str, str],
        phone: str,
        hours: dict[str, str] | None = None,
        **kwargs: Any,
    ) -> Script:
        """Generate LocalBusiness structured data (JSON-LD).

        Args:
            name: Business name
            address: Address dict with keys: street, city, state, zip, country
            phone: Phone number
            hours: Opening hours dict (day: hours), e.g., {"Monday": "9:00-17:00"}
            **kwargs: Additional Schema.org LocalBusiness properties

        Returns:
            Script element with JSON-LD structured data

        Example:
            >>> StructuredData.local_business(
            ...     name="Joe's Coffee Shop",
            ...     address={
            ...         "street": "123 Main St",
            ...         "city": "Springfield",
            ...         "state": "IL",
            ...         "zip": "62701",
            ...         "country": "US"
            ...     },
            ...     phone="+1-555-123-4567",
            ...     hours={"Monday-Friday": "7:00-19:00", "Saturday-Sunday": "8:00-17:00"}
            ... )
        """
        data: dict[str, Any] = {
            "@context": "https://schema.org",
            "@type": "LocalBusiness",
            "name": name,
            "address": {
                "@type": "PostalAddress",
                "streetAddress": address.get("street", ""),
                "addressLocality": address.get("city", ""),
                "addressRegion": address.get("state", ""),
                "postalCode": address.get("zip", ""),
                "addressCountry": address.get("country", ""),
            },
            "telephone": phone,
        }

        if hours:
            # Convert hours dict to OpeningHoursSpecification
            opening_hours = []
            for days, time_range in hours.items():
                normalized_days = _expand_day_spec(days)
                day_of_week: str | list[str]
                if normalized_days:
                    day_of_week = [_schema_day_uri(day) for day in normalized_days]
                    if len(day_of_week) == 1:
                        day_of_week = day_of_week[0]
                else:
                    day_of_week = days

                opening_hours.append(
                    {
                        "@type": "OpeningHoursSpecification",
                        "dayOfWeek": day_of_week,
                        "opens": time_range.split("-")[0] if "-" in time_range else time_range,
                        "closes": time_range.split("-")[1] if "-" in time_range else time_range,
                    }
                )
            data["openingHoursSpecification"] = opening_hours

        # Add any additional properties
        data.update(kwargs)

        return Script(
            json.dumps(data, indent=2),
            type="application/ld+json",
        )

article(headline, description, image, author, published, modified=None, **kwargs) staticmethod

Generate Article structured data (JSON-LD).

Parameters:

Name Type Description Default
headline str

Article headline/title

required
description str

Article description

required
image str

Article image URL

required
author str

Author name

required
published str

Published date (ISO 8601 format)

required
modified str | None

Modified date (ISO 8601 format), optional

None
**kwargs Any

Additional Schema.org Article properties

{}

Returns:

Type Description
Script

Script element with JSON-LD structured data

Example

StructuredData.article( ... headline="10 Tips for Better Python Code", ... description="Learn how to write cleaner Python", ... image="https://example.com/python-tips.jpg", ... author="Jane Smith", ... published="2026-02-12T10:00:00Z", ... modified="2026-02-12T14:30:00Z" ... )

Source code in src/faststrap/seo/structured_data.py
@staticmethod
def article(
    headline: str,
    description: str,
    image: str,
    author: str,
    published: str,
    modified: str | None = None,
    **kwargs: Any,
) -> Script:
    """Generate Article structured data (JSON-LD).

    Args:
        headline: Article headline/title
        description: Article description
        image: Article image URL
        author: Author name
        published: Published date (ISO 8601 format)
        modified: Modified date (ISO 8601 format), optional
        **kwargs: Additional Schema.org Article properties

    Returns:
        Script element with JSON-LD structured data

    Example:
        >>> StructuredData.article(
        ...     headline="10 Tips for Better Python Code",
        ...     description="Learn how to write cleaner Python",
        ...     image="https://example.com/python-tips.jpg",
        ...     author="Jane Smith",
        ...     published="2026-02-12T10:00:00Z",
        ...     modified="2026-02-12T14:30:00Z"
        ... )
    """
    data = {
        "@context": "https://schema.org",
        "@type": "Article",
        "headline": headline,
        "description": description,
        "image": image,
        "author": {"@type": "Person", "name": author},
        "datePublished": published,
    }

    if modified:
        data["dateModified"] = modified

    # Add any additional properties
    data.update(kwargs)

    return Script(
        json.dumps(data, indent=2),
        type="application/ld+json",
    )

breadcrumb(items) staticmethod

Generate BreadcrumbList structured data (JSON-LD).

Parameters:

Name Type Description Default
items list[tuple[str, str]]

List of (name, url) tuples representing breadcrumb items

required

Returns:

Type Description
Script

Script element with JSON-LD structured data

Example

StructuredData.breadcrumb([ ... ("Home", "https://example.com/"), ... ("Products", "https://example.com/products"), ... ("Laptops", "https://example.com/products/laptops") ... ])

Source code in src/faststrap/seo/structured_data.py
@staticmethod
def breadcrumb(items: list[tuple[str, str]]) -> Script:
    """Generate BreadcrumbList structured data (JSON-LD).

    Args:
        items: List of (name, url) tuples representing breadcrumb items

    Returns:
        Script element with JSON-LD structured data

    Example:
        >>> StructuredData.breadcrumb([
        ...     ("Home", "https://example.com/"),
        ...     ("Products", "https://example.com/products"),
        ...     ("Laptops", "https://example.com/products/laptops")
        ... ])
    """
    list_items = [
        {
            "@type": "ListItem",
            "position": i + 1,
            "name": name,
            "item": url,
        }
        for i, (name, url) in enumerate(items)
    ]

    data = {
        "@context": "https://schema.org",
        "@type": "BreadcrumbList",
        "itemListElement": list_items,
    }

    return Script(
        json.dumps(data, indent=2),
        type="application/ld+json",
    )

local_business(name, address, phone, hours=None, **kwargs) staticmethod

Generate LocalBusiness structured data (JSON-LD).

Parameters:

Name Type Description Default
name str

Business name

required
address dict[str, str]

Address dict with keys: street, city, state, zip, country

required
phone str

Phone number

required
hours dict[str, str] | None

Opening hours dict (day: hours), e.g., {"Monday": "9:00-17:00"}

None
**kwargs Any

Additional Schema.org LocalBusiness properties

{}

Returns:

Type Description
Script

Script element with JSON-LD structured data

Example

StructuredData.local_business( ... name="Joe's Coffee Shop", ... address={ ... "street": "123 Main St", ... "city": "Springfield", ... "state": "IL", ... "zip": "62701", ... "country": "US" ... }, ... phone="+1-555-123-4567", ... hours={"Monday-Friday": "7:00-19:00", "Saturday-Sunday": "8:00-17:00"} ... )

Source code in src/faststrap/seo/structured_data.py
@staticmethod
def local_business(
    name: str,
    address: dict[str, str],
    phone: str,
    hours: dict[str, str] | None = None,
    **kwargs: Any,
) -> Script:
    """Generate LocalBusiness structured data (JSON-LD).

    Args:
        name: Business name
        address: Address dict with keys: street, city, state, zip, country
        phone: Phone number
        hours: Opening hours dict (day: hours), e.g., {"Monday": "9:00-17:00"}
        **kwargs: Additional Schema.org LocalBusiness properties

    Returns:
        Script element with JSON-LD structured data

    Example:
        >>> StructuredData.local_business(
        ...     name="Joe's Coffee Shop",
        ...     address={
        ...         "street": "123 Main St",
        ...         "city": "Springfield",
        ...         "state": "IL",
        ...         "zip": "62701",
        ...         "country": "US"
        ...     },
        ...     phone="+1-555-123-4567",
        ...     hours={"Monday-Friday": "7:00-19:00", "Saturday-Sunday": "8:00-17:00"}
        ... )
    """
    data: dict[str, Any] = {
        "@context": "https://schema.org",
        "@type": "LocalBusiness",
        "name": name,
        "address": {
            "@type": "PostalAddress",
            "streetAddress": address.get("street", ""),
            "addressLocality": address.get("city", ""),
            "addressRegion": address.get("state", ""),
            "postalCode": address.get("zip", ""),
            "addressCountry": address.get("country", ""),
        },
        "telephone": phone,
    }

    if hours:
        # Convert hours dict to OpeningHoursSpecification
        opening_hours = []
        for days, time_range in hours.items():
            normalized_days = _expand_day_spec(days)
            day_of_week: str | list[str]
            if normalized_days:
                day_of_week = [_schema_day_uri(day) for day in normalized_days]
                if len(day_of_week) == 1:
                    day_of_week = day_of_week[0]
            else:
                day_of_week = days

            opening_hours.append(
                {
                    "@type": "OpeningHoursSpecification",
                    "dayOfWeek": day_of_week,
                    "opens": time_range.split("-")[0] if "-" in time_range else time_range,
                    "closes": time_range.split("-")[1] if "-" in time_range else time_range,
                }
            )
        data["openingHoursSpecification"] = opening_hours

    # Add any additional properties
    data.update(kwargs)

    return Script(
        json.dumps(data, indent=2),
        type="application/ld+json",
    )

organization(name, url, logo, social_links=None, **kwargs) staticmethod

Generate Organization structured data (JSON-LD).

Parameters:

Name Type Description Default
name str

Organization name

required
url str

Organization website URL

required
logo str

Organization logo URL

required
social_links list[str] | None

List of social media profile URLs

None
**kwargs Any

Additional Schema.org Organization properties

{}

Returns:

Type Description
Script

Script element with JSON-LD structured data

Example

StructuredData.organization( ... name="Acme Corp", ... url="https://acme.com", ... logo="https://acme.com/logo.png", ... social_links=[ ... "https://twitter.com/acmecorp", ... "https://linkedin.com/company/acmecorp" ... ] ... )

Source code in src/faststrap/seo/structured_data.py
@staticmethod
def organization(
    name: str,
    url: str,
    logo: str,
    social_links: list[str] | None = None,
    **kwargs: Any,
) -> Script:
    """Generate Organization structured data (JSON-LD).

    Args:
        name: Organization name
        url: Organization website URL
        logo: Organization logo URL
        social_links: List of social media profile URLs
        **kwargs: Additional Schema.org Organization properties

    Returns:
        Script element with JSON-LD structured data

    Example:
        >>> StructuredData.organization(
        ...     name="Acme Corp",
        ...     url="https://acme.com",
        ...     logo="https://acme.com/logo.png",
        ...     social_links=[
        ...         "https://twitter.com/acmecorp",
        ...         "https://linkedin.com/company/acmecorp"
        ...     ]
        ... )
    """
    data: dict[str, Any] = {
        "@context": "https://schema.org",
        "@type": "Organization",
        "name": name,
        "url": url,
        "logo": logo,
    }

    if social_links:
        data["sameAs"] = social_links

    # Add any additional properties
    data.update(kwargs)

    return Script(
        json.dumps(data, indent=2),
        type="application/ld+json",
    )

product(name, description, image, price, currency='USD', rating=None, review_count=None, availability='InStock', **kwargs) staticmethod

Generate Product structured data (JSON-LD).

Parameters:

Name Type Description Default
name str

Product name

required
description str

Product description

required
image str

Product image URL

required
price str

Product price (as string)

required
currency str

Currency code (default: "USD")

'USD'
rating float | None

Average rating (0-5)

None
review_count int | None

Number of reviews

None
availability str

Availability status (InStock, OutOfStock, PreOrder)

'InStock'
**kwargs Any

Additional Schema.org Product properties

{}

Returns:

Type Description
Script

Script element with JSON-LD structured data

Example

StructuredData.product( ... name="Wireless Headphones", ... description="Premium noise-cancelling headphones", ... image="https://example.com/headphones.jpg", ... price="199.99", ... currency="USD", ... rating=4.5, ... review_count=342, ... availability="InStock" ... )

Source code in src/faststrap/seo/structured_data.py
@staticmethod
def product(
    name: str,
    description: str,
    image: str,
    price: str,
    currency: str = "USD",
    rating: float | None = None,
    review_count: int | None = None,
    availability: str = "InStock",
    **kwargs: Any,
) -> Script:
    """Generate Product structured data (JSON-LD).

    Args:
        name: Product name
        description: Product description
        image: Product image URL
        price: Product price (as string)
        currency: Currency code (default: "USD")
        rating: Average rating (0-5)
        review_count: Number of reviews
        availability: Availability status (InStock, OutOfStock, PreOrder)
        **kwargs: Additional Schema.org Product properties

    Returns:
        Script element with JSON-LD structured data

    Example:
        >>> StructuredData.product(
        ...     name="Wireless Headphones",
        ...     description="Premium noise-cancelling headphones",
        ...     image="https://example.com/headphones.jpg",
        ...     price="199.99",
        ...     currency="USD",
        ...     rating=4.5,
        ...     review_count=342,
        ...     availability="InStock"
        ... )
    """
    data: dict[str, Any] = {
        "@context": "https://schema.org",
        "@type": "Product",
        "name": name,
        "description": description,
        "image": image,
        "offers": {
            "@type": "Offer",
            "price": price,
            "priceCurrency": currency,
            "availability": f"https://schema.org/{availability}",
        },
    }

    if rating is not None and review_count is not None:
        data["aggregateRating"] = {
            "@type": "AggregateRating",
            "ratingValue": rating,
            "reviewCount": review_count,
        }

    # Add any additional properties
    data.update(kwargs)

    return Script(
        json.dumps(data, indent=2),
        type="application/ld+json",
    )