---
title: "API Contract"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{API Contract}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
# API Contract for `shinyseo`
This page is written so another agent can use the package without reading the source.
## Exported surface
- `social_meta(meta)` — builds and injects metadata tags into the page `
` at startup.
- `update_meta(session, title, description, url, image)` — reactively updates one or more metadata fields from the server at runtime.
- `write_manifest(meta, path, display, start_url, background_color)` — writes a `manifest.json` to `www/` so Shiny can serve it.
## Return value
`social_meta()` returns a `shiny::tags$head()` fragment.
You normally place it inside a Shiny UI definition, for example:
```r
shinyseo::social_meta(list(
title = "Hello",
description = "Short app description.",
url = "https://example.no",
image = "https://example.no/share.png"
))
```
## Input contract
`meta` can be one of:
- a character string containing a path to a YAML file
- a named list of metadata values
If `meta` is a character string, the package reads it with `yaml::read_yaml()`.
If the file does not exist, the function stops with:
- `Meta file not found: `
## Required fields
These keys must exist in the final metadata object:
- `title`
- `description`
- `url`
- `image`
The function checks only that the names exist. It does not currently validate that the values are non-empty strings.
If any required key is missing, the function stops with:
- `Missing required meta fields: ...`
## Defaults
The package fills these defaults when the key is missing or `NULL`:
- `locale = "en_US"`
- `robots = "index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1"`
- `twitter_card = "summary_large_image"`
- `bing_site_verification = Sys.getenv("SHINYSEO_BING_SITE_VERIFICATION")` when present
- `twitter_site = Sys.getenv("SHINYSEO_TWITTER_SITE")` when present
- `twitter_creator = Sys.getenv("SHINYSEO_TWITTER_CREATOR")` when present
- `schema_type = "WebApplication"`
- `operating_system = "Any"`
- `author_type = "Person"`
- `publisher_type = "Organization"`
- `in_language = locale`
Important detail:
- `schema` is enabled unless it is exactly `FALSE`
- `schema = TRUE`, `schema = NULL`, or an omitted `schema` field all result in JSON-LD output
- `schema = FALSE` suppresses JSON-LD
- The helper does not emit a `` tag; set the document title in the app
UI so it does not clash with any title Shiny already sets
## Output tags
The function writes these tags into the returned head fragment:
- ``
- ``
- ``
- Open Graph tags used by Facebook, LinkedIn, Slack, X, and other platforms that read Open Graph:
- `og:type = website`
- `og:title`
- `og:description`
- `og:url`
- `og:image`
- `og:site_name` when `site_name` is present
- `og:locale` when `locale` is present
- `og:image:width` when `image_width` is present
- `og:image:height` when `image_height` is present
- `og:image:type` when `image_type` is present
- `og:image:alt` when `image_alt` is present
- Twitter Card tags:
- `twitter:card`
- `twitter:title`
- `twitter:description`
- `twitter:image`
- `twitter:url`
- `twitter:site` when `twitter_site` is present
- `twitter:creator` when `twitter_creator` is present
- `twitter:image:alt` when `twitter_image_alt` is present
- Bing verification:
- `msvalidate.01` when `bing_site_verification` is present
- Google verification:
- `google-site-verification` when `google_site_verification` is present
- Yandex verification:
- `yandex-verification` when `yandex_site_verification` is present
- Baidu verification:
- `baidu-site-verification` when `baidu_site_verification` is present
- Naver verification:
- `naver-site-verification` when `naver_site_verification` is present
- Facebook domain verification:
- `facebook-domain-verification` when `facebook_domain_verification` is present
- Pinterest domain verification:
- `p:domain_verify` when `pinterest_domain_verification` is present
- Schema.org JSON-LD:
- only when `schema` is not `FALSE`
- always includes `@context`, `@type`, `name`, `description`, `url`, and `inLanguage`
- conditionally includes `applicationCategory`, `operatingSystem`, `educationalUse`, `isAccessibleForFree`, `disclaimer`, `author`, and `publisher`
## Field map
| Key | Used for |
| --- | --- |
| `title` | Open Graph title, Twitter title, schema name |
| `description` | Meta description, Open Graph description, Twitter description, schema description |
| `url` | Canonical URL, Open Graph URL, Twitter URL, schema URL |
| `image` | Open Graph image, Twitter image |
| `locale` | Open Graph locale, schema language default |
| `robots` | Robots meta tag |
| `twitter_card` | Twitter card type |
| `twitter_site` | Twitter site handle; falls back to `SHINYSEO_TWITTER_SITE` |
| `twitter_creator` | Twitter creator handle; falls back to `SHINYSEO_TWITTER_CREATOR` |
| `twitter_image_alt` | Twitter image alt text |
| `image_alt` | Open Graph image alt text |
| `image_width` | Open Graph image width |
| `image_height` | Open Graph image height |
| `image_type` | Open Graph image MIME type |
| `bing_site_verification` | Bing Webmaster verification; falls back to `SHINYSEO_BING_SITE_VERIFICATION` |
| `google_site_verification` | Google Search Console verification |
| `yandex_site_verification` | Yandex Webmaster verification |
| `baidu_site_verification` | Baidu Webmaster verification |
| `naver_site_verification` | Naver Webmaster verification |
| `facebook_domain_verification` | Facebook domain verification |
| `pinterest_domain_verification` | Pinterest domain verification |
| `schema_type` | Schema.org `@type` |
| `application_category` | Schema.org `applicationCategory` |
| `operating_system` | Schema.org `operatingSystem` |
| `educational_use` | Schema.org `educationalUse` |
| `is_accessible_for_free` | Schema.org `isAccessibleForFree` |
| `disclaimer` | Schema.org `disclaimer` |
| `author_name` | Schema.org `author.name` |
| `author_type` | Schema.org `author.@type` |
| `publisher_name` | Schema.org `publisher.name` |
| `publisher_type` | Schema.org `publisher.@type` |
| `in_language` | Schema.org `inLanguage` |
| `schema` | Turns JSON-LD on or off |
## Favicon and web app manifest fields
These optional fields in `social_meta()` control browser icon and PWA behaviour:
| Field | Tag produced |
| --- | --- |
| `favicon` | `` |
| `favicon_type` | Overrides the MIME type inferred from `favicon`'s extension |
| `favicon_png` | `` — PNG fallback alongside an SVG `favicon` |
| `favicon_png_sizes` | Overrides the `sizes` attribute on `favicon_png` (defaults to `"32x32"`) |
| `apple_touch_icon` | `` |
| `manifest` | `` |
| `theme_color` | `` |
| `short_name` | Used by `write_manifest()` for the manifest `short_name` field |
MIME type is inferred automatically from the file extension of `favicon` (`.ico`, `.png`, `.svg`, `.webp`, `.gif`). Supply `favicon_type` to override.
If `favicon` is an SVG, also set `favicon_png` to a PNG fallback. Chromium-based browsers don't render SVG favicons in the address bar and fall back to a generic globe icon without one.
### `write_manifest()`
Generates `www/manifest.json` from the same metadata object. Call it once in `global.R`:
```r
shinyseo::write_manifest(list(
title = "My App",
short_name = "MyApp",
description = "A Shiny web app.",
theme_color = "#1a73e8",
favicon = "/favicon.png",
apple_touch_icon = "/apple-touch-icon.png"
))
```
Then reference the manifest from the UI:
```r
shinyseo::social_meta(list(
...,
manifest = "/manifest.json"
))
```
Parameters:
- `meta` — same YAML path or list as `social_meta()`.
- `path` — directory to write into. Defaults to `"www"`.
- `display` — PWA display mode: `"standalone"` (default), `"fullscreen"`, `"minimal-ui"`, or `"browser"`.
- `start_url` — start URL in the manifest. Defaults to `"/"`.
- `background_color` — splash screen background. Defaults to `theme_color` when set, otherwise `"#ffffff"`.
## `update_meta()` — reactive metadata updates
`update_meta(session, title, description, url, image)` sends a custom message to the browser that updates the live DOM without a page reload. It is intended for multi-page or tabbed Shiny apps where the metadata should reflect the current view.
### Parameters
- `session` — the Shiny `session` object passed to `server`.
- `title` — replaces `document.title`, `og:title`, and `twitter:title`.
- `description` — replaces `meta[name="description"]`, `og:description`, and `twitter:description`.
- `url` — replaces `link[rel="canonical"]`, `og:url`, and `twitter:url`.
- `image` — replaces `og:image` and `twitter:image`.
All parameters except `session` are optional. Only the fields you supply are changed; omitted fields keep their current values. If no fields are supplied the function is a no-op.
### Requirements
`social_meta()` must be present in the UI for the JavaScript handler to be registered. `update_meta()` alone has no effect.
### Example
```r
server <- function(input, output, session) {
observeEvent(input$tabs, {
if (input$tabs == "about") {
shinyseo::update_meta(session,
title = "About – My App",
description = "Learn more about the team behind the app."
)
}
})
}
```
## Behavior notes
- The function does not mutate the input list outside its local copy.
- The helper returns plain Shiny tags, so it is safe to embed in `fluidPage()`, `navbarPage()`, or any other UI container that accepts head tags.
- The package is intentionally small and generic.
- If you need a simpler SEO-only configuration, you can still use the required four fields and leave the rest blank.