---
title: SC Breadcrumb
description: An accessible breadcrumb trail from URL segments, with JSON-LD for SEO.
url: https://secretocho.design/lab/sc-breadcrumb/
type: SoftwareSourceCode
status: working
published: 2025-08-28
updated: 2025-08-28
---

# SC Breadcrumb

> An accessible breadcrumb trail from URL segments, with JSON-LD for SEO.

component 03 / 28 · WORKING · featured

- **type**: Code Component
- **platform**: Framer
- **last update**: 2025-08-28
- **read time**: 5–6 min
- **license**: SecretoCho Design

![A still of the SC Breadcrumb demo, as its module draws it with reduced motion.](https://secretocho.design/_astro/still.mwACvl80_1Imnec.webp)

*fig 00 · live demo, ported from **SC\_Breadcrumb.tsx***

Builds an accessible breadcrumb trail from URL segments with configurable separators/alignment and emits JSON-LD (BreadcrumbList/optional BlogPosting) for SEO.

SC Breadcrumb builds a clean, responsive breadcrumb trail from the current URL, with configurable separator, max items (auto-ellipsis), alignment, and typography. It can also output schema.org JSON-LD for SEO (BreadcrumbList + optional BlogPosting), with empty-field cleanup for tidy markup.

- Navigation
- SEO

## Main

### Overview

- **Built With**: React, CSS, Framer Code Component API
- **Layout Support**: Fully Responsive (any width/height)
- **Orientation**: Inline nav trail
- **Ideal Use Case**: Page trails, article nav, SEO-friendly navigation

### Tags

- breadcrumb
- navigation
- seo
- schema.org
- json-ld
- accessibility

### Code comment

```tsx
/**
 * SC Breadcrumb
 * Dynamic breadcrumb trail + optional schema.org JSON-LD.
 * © SecretoCho Design – Aug 2025
 * @framerDisableUnlink
 * @framerSupportedLayoutWidth any
 * @framerSupportedLayoutHeight any
 */
```

### Dependencies

- **Technologies**: None (pure React + DOM)
- **Notes**: No runtime libraries required; works with Framer’s standard runtime.

### Complexity

- **Logic Level**: Low–Medium (path parsing + schema build)
- **Runtime Cost**: Very Low (simple text/links)
- **Component Size**: ~2–3 KB (raw TSX)
- **Notes**: JSON-LD injection is static; no reflow cost after render.

## Guide

### Instructions

1. Drop **SCBreadcrumb** into your page header area.
2. Set **Separator** (e.g., /, ›, →).
3. Choose **Max Items** (older items collapse to …).
4. Pick **Alignment** (Left/Center/Right).
5. (Optional) Style with the **Font**, **Color**, **Hover** controls.
6. (Optional) Enable SEO: fill **Breadcrumb Name/Slug/Root** and/or **Blog** fields to emit JSON-LD.

### Examples / Presets

- Classic
  
  - **separator**: "/"
  - **maxItems**: 3
  - **color**: #555
  - **hover**: #0A7
  
- Chevrons
  
  separator="›" · maxItems=4 · center
  
- Minimal
  
  separator="•" · maxItems=2 · right
  

## Mechanics

### How it Works

- **Path parsing**: window.location.pathname → /a/b/c → \['a','b','c'\].
- **Formatting**: formatTitle() decodes URI, swaps -/\_ with spaces, Title-Cases words.
- **Overflow**: If segments.length \> maxItems, prepend … and keep the last maxItems segments.
- **Link building**: Each non-last crumb links to its cumulative absolute URL (origin + /a/...).
- **Schema**:
  - **BreadcrumbList** uses Root → Name/Slug → (optional) Headline.
  - **BlogPosting** uses blog fields (author/publisher/logo/image/dates).
  - cleanObject() removes empty strings/arrays/objects before JSON-LD injection.

### Performance & Tuning

- Text-only render; virtually zero layout cost.
- Avoid extremely long labels; rely on maxItems to keep trails short.
- Hover color changes are GPU-friendly; transition is limited to color.

### Compatibility & Limits

- Requires window.location (client render). For SSR, outputs a safe fallback (/) until hydrated.
- JSON-LD is appended via \<script type="application/ld+json"\>; safe in all evergreen browsers.
- Ellipsis crumb … is non-interactive by design.

## Quality

### Accessibility

- Use inside a \<nav aria-label="Breadcrumb"\> region when possible.
- Last item should not be a link; mark it as the current page (aria-current="page" if wrapped in an anchor).
- Separators are decorative—render as text with aria-hidden="true" or CSS ::before.

### Security & Privacy

- No network calls or storage.
- JSON-LD strings are serialized, not executed; still ensure values come from trusted sources (avoid piping untrusted user input directly).
- Uses window.location only; no cookies or tracking.
- Small surface area; no global listeners.

## Reference

### Props

| name | type | default | description |
| --- | --- | --- | --- |
| font | Font | {"Inter",400,"1rem"} | Typography for items |
| color | string | #555 | Link/label color |
| hoverColor | string | #009658 | Hover color |
| separator | string | / | Character between crumbs (e.g., / › →) |
| maxItems | number | 3 | Max visible segments (older collapse to …) |
| gap | number | 8 | Space between crumbs (px) |
| alignment | enum | flex-start | Flex alignment (flex-start\|center\|flex-end) |
| sectionId | string | breadcrumb-section | Wrapper id attribute |
| schemaName | string | "" | Breadcrumb display name (e.g., “Blog”) |
| schemaSlug | string | "" | Breadcrumb slug (e.g., "blog") |
| schemaRoot | string | Home | Root label for position 1 |
| blogHeadline | string | "" | Article headline |
| blogDescription | string | "" | Short description |
| blogImage | image | "" | Primary image URL |
| blogImageHeight | number | 800 | Image height (px) |
| blogImageWidth | number | 1200 | Image width (px) |
| blogAuthorName | string | "" | Author full name |
| blogAuthorURL | string | "" | Author profile/home URL |
| blogPublisherName | string | "" | Publisher name |
| blogPublisherLogo | image | "" | Publisher logo URL |
| blogPublisherLogoHeight | number | 60 | Logo height (px) |
| blogPublisherLogoWidth | number | 600 | Logo width (px) |
| blogPublishDate | string | "" | ISO date published (YYYY-MM-DD) |
| blogModifiedDate | string | "" | ISO date modified (YYYY-MM-DD) |

### Notes & Tips

- **Overflow UX**: The … represents skipped ancestors; keep maxItems 2–4 for clarity.
- **Chevrons**: For a chevron look, set separator="›" and tighten gap=6.
- **Centering**: For centered trails within fluid layouts, ensure the parent has display:block with adequate width.
- **Framer control quirk**: Alignment value should be flex-start/center/flex-end (the UI shows “Left/Center/Right”).

### Troubleshooting / FAQ

My alignment doesn’t change.

Use flex-start|center|flex-end; if imported presets use "left", switch to flex-start.

Labels look odd (“My%20Post”).

The component decodes URI; if still wrong, confirm your route slugs.

Home links to a relative path.

Origin comes from window.location.origin; verify your domain/protocol in previews.

JSON-LD not visible.

It won’t render UI—check page source (client) or DevTools Elements for \<script type="application/ld+json"\>.

## Source

### Header comment

*SC\_Breadcrumb.tsx · lines 15–22 · header comment*

```tsx
/**
 * SC Dynamic Breadcrumb
 * ---------------------
 * - URL-based breadcrumb with overflow handling
 * - SPA navigation (single boolean control)
 * - BreadcrumbList + BlogPosting JSON-LD (optional)
 * - Canvas-safe preview path
 */
```

[props \[REF\]](#reference)

### Key excerpt

The trail from the URL: segments past the limit fold into “…”, each crumb links to its own path prefix, and a BreadcrumbList is built for JSON-LD.

*SC\_Breadcrumb.tsx · lines 187–238 · tsx*

```tsx
    // -------- Build items --------
    const segments = pathForBuild.split("/").filter(Boolean)
    const effectiveMax = Math.max(1, Math.min(layoutMax, 50))
    const hasOverflow = segments.length > effectiveMax
    const visibleSegs = hasOverflow ? segments.slice(-effectiveMax) : segments
    const all = ["home", ...(hasOverflow ? ["..."] : []), ...visibleSegs]

    const buildHref = (idx: number): string | null => {
        const isLast = idx === all.length - 1
        const seg = all[idx]
        if (seg === "..." || isLast) return null
        if (seg === "home") return origin
        const ahead = all.length - idx - 1
        const segCount = segments.length - ahead
        return `${origin}/${segments.slice(0, segCount).join("/")}`
    }

    const items = all.map((seg, i) => ({
        key: i,
        segment: seg,
        label:
            seg === "home" ? "Home" : seg === "..." ? "…" : formatSegment(seg),
        href: buildHref(i),
        isLast: i === all.length - 1,
    }))

    // -------- Schema --------
    const breadcrumbSchema = breadcrumbEnabled
        ? cleanObject({
              "@context": "https://schema.org",
              "@type": "BreadcrumbList",
              itemListElement: [
                  {
                      "@type": "ListItem",
                      position: 1,
                      name: breadcrumbRootName,
                      item: origin,
                  },
                  breadcrumbCategoryName && {
                      "@type": "ListItem",
                      position: 2,
                      name: breadcrumbCategoryName,
                      item: `${origin}/${breadcrumbCategorySlug}`,
                  },
                  blogHeadline && {
                      "@type": "ListItem",
                      position: 3,
                      name: blogHeadline,
                  },
              ].filter(Boolean),
          })
        : null
```

## Credits

### Credits

Designed and implemented by **SecretoCho Design**. Pattern-safe URL formatting and schema cleanup helpers included.

- **license**: SecretoCho Design

SecretoCho Design 2025

### Disclaimer

Provided “as is” for navigation/SEO convenience. Validate structured data in your environment (Search Console/Rich Results Test) and ensure all values (images, author, publisher) are licensed and accurate.

## Proves

- [Creative coding](https://secretocho.design/about/#capabilities)

## Hiring a design engineer?

I’m open to senior design engineer roles: remote from Belgrade, or relocation with visa sponsorship. Write to me, or read the CV first. Client work stays selective, one project at a time.

[sc@secretocho.design](mailto:sc@secretocho.design)

[read the CV](https://secretocho.design/cv/) · [LinkedIn](https://www.linkedin.com/in/secretocho/)
