ScreenshotNeo

BlogHow-to

How to Configure Loki Screenshot Tests for an Indian-Language Storybook

Configure Loki to catch visual regressions in an Indian-language Storybook, with script-specific fonts, reliable capture timing, CI checks, and troubleshooting.

By the ScreenshotNeo team4 October 20269 min read

To configure Loki screenshot tests for an Indian-language Storybook, load the font for the actual script in Storybook’s preview iframe, make the stories and browser environment deterministic, then create and review Loki reference images. In CI, require references so a missing baseline fails instead of being silently created. For custom capture code, wait for document.fonts.ready before taking a screenshot; Loki’s documentation does not establish a universal font-wait configuration key.

The exact language, font, framework, and package versions matter. The examples below are adaptable setup guidance, not a tested recipe for a particular repository. Check the installed Loki and Storybook versions before copying commands because their documentation and APIs can change.

1. Check the project and install Loki

Loki’s getting-started guide documents Node.js 16 or newer, installation as a development dependency, and initialization that detects the project type and adds defaults to package.json. Use your project’s package manager consistently:

yarn add loki --dev
yarn loki init

For npm, the equivalent package installation is:

npm install --save-dev loki
npx loki init

Inspect the generated scripts and Loki configuration rather than assuming initialization selected the right browser target. For web projects, Loki recommends Chrome in Docker; local Chrome is also supported. Loki does not start Storybook for you: you must start its server or build and serve the static Storybook before running comparisons. See Loki’s getting-started guide and its documentation.

2. Load the intended script font in the preview iframe

Storybook stories render in a preview iframe, separate from the surrounding manager interface. Put font links in .storybook/preview-head.html so they load where the components render. Storybook documents this file as a place for head markup such as stylesheet links and preload links. See Storybook: Story rendering.

<!-- .storybook/preview-head.html -->
<link rel="stylesheet" href="/fonts/app-fonts.css">

Make sure the font file is actually served to the preview. For example, if the repository serves a public directory at the site root, the stylesheet and font paths must resolve from that root in both local and static builds. Verify the network request in the browser target Loki uses; a link that works in the Storybook manager does not prove the iframe loaded it.

Choose a stack for the language and visual design in your interface. Noto’s examples include this Hindi-oriented stack and a Tamil-specific family:

/* Example only: adapt family names to the font files and language in use. */
.story-copy--hi {
  font-family: "Noto Sans Devanagari", "Noto Sans", "Noto Sans Symbols 2", sans-serif;
}

.story-copy--ta {
  font-family: "Noto Sans Tamil", "Noto Sans", "Noto Sans Symbols 2", sans-serif;
}

Do not treat a Hindi font stack as a universal stack for Indian languages. Select the typeface for the script your UI needs, and include deliberate fallbacks for symbols and any other characters in the content. CSS tries later families when an earlier family does not provide a needed character; that can mask a missing intended font. Keep a diagnostic story where the intended family and rendered appearance can be checked. Refer to Noto’s font guidance and families.

A preload may make a local font available earlier, but it is not proof that the right face was used. If you add one, ensure the URL, format, and cross-origin setting match the font asset and stylesheet. Confirm the computed family and inspect the actual font request in the browser used for capture.

3. Make representative stories and capture the first references

Start with a small set of stories that expose likely regressions: the actual language strings your interface displays, representative punctuation and numerals, different text sizes, and layouts where wrapping or clipping matters. Use approved product copy. A few stories can reveal problems in those states, but do not assume they establish coverage of every word or language variation.

Start Storybook, then generate the initial references:

# Terminal 1: run the project's existing Storybook command.
yarn storybook

# Terminal 2: create Loki reference screenshots.
yarn loki update

Review the generated images to verify the intended font, glyph rendering, line breaks, and layout before treating them as the reference. Then run the comparison:

yarn loki test

Loki stores captured screenshots and differences in its documented loki/current and loki/difference folders. Inspect the diff and current capture. Update references with yarn loki update only after deciding that the visual change is intentional, then commit the approved reference changes. Loki documents Git LFS as an option for reference images. See Loki command-line options.

4. Wait for fonts when custom capture timing is involved

A capture made before web fonts load can show fallback glyphs, different line breaks, or a shifted layout. The browser’s document.fonts.ready promise fulfills after loading and layout operations for used fonts finish. See MDN: Document.fonts.

For custom browser automation that controls the page, wait inside the page before taking the screenshot. This is browser-side JavaScript guidance, not a Loki configuration option:

await page.evaluate(async () => {
  await document.fonts.ready;
});
await page.screenshot({ path: "story.png", fullPage: true });

For a Playwright page, the snippet can be used after navigating to the rendered story and before its screenshot call. If your setup uses a different browser automation API, perform the equivalent page-context wait. Storybook’s test-runner documentation also provides waitForPageReady(page) for readiness work, including assets such as fonts; it applies to that runner’s workflow and should not be assumed to be a Loki hook. See Storybook test runner documentation.

Loki handles common loading cases, but if an external or asynchronous font remains intermittent, verify readiness in the actual Loki browser target. If your installed Loki version has no convenient hook for the desired gate, use local, deterministic font assets and make the story render only after its required state is ready.

5. Run Loki in CI with required references

Build a static Storybook and have Loki compare it with committed references. Loki’s CI guide shows a static-build pattern using --requireReference and a file URI:

yarn build-storybook
 yarn loki --requireReference --reactUri file:./storybook-static

Remove the leading space before yarn loki if copying the snippet into a shell script; the command is shown on a separate line for readability. Alternatively, define a script without indentation:

{
  "scripts": {
    "build-storybook": "storybook build",
    "test:visual": "loki --requireReference --reactUri file:./storybook-static"
  }
}
yarn build-storybook
yarn test:visual

The --requireReference flag makes a missing baseline fail rather than allowing CI to create one as if it were an approved change. Verify the URI option for your framework and installed Loki release; the documented example uses --reactUri. See Loki’s continuous integration guide.

6. Keep captures stable and review diffs carefully

  • Hold the environment constant: use the same browser target, viewport configuration, font assets, story content, and story state when comparing references.
  • Make inputs deterministic: avoid live data and time-dependent content in visual stories, or provide stable fixtures.
  • Inspect the right things: check glyph coverage and shaping, the loaded family, line breaks, clipping, and surrounding layout—not just whether a diff is small.
  • Handle animation deliberately: Loki says it disables common CSS transitions and animations and requestAnimationFrame by default. Looped animation frames, GIFs, SVG animations, and React Native Animated are documented limitations. Skip unsuitable stories with loki: { skip: true }, or use the documented asynchronous callback pattern when a story must signal completion.
  • Approve intentional updates: a repeatable screenshot can still consistently show the wrong fallback font. Establish correctness before updating references.

For the exact options supported by your release, check Loki’s configuration and command-line reference. Loki’s docs describe Chrome in Docker, local Chrome, iOS simulator, and Android emulator targets; choose only targets your project actually supports and needs to validate.

7. Troubleshoot common failures

Symptom Likely cause What to check or fix
Boxes or fallback glyphs appear The intended font file did not load, lacks the characters, or is not selected in the preview iframe. Check the iframe’s stylesheet and font requests, asset paths in the static build, the CSS family name, and script coverage. Add the correct script-specific face and a deliberate fallback.
Local screenshots look right but CI differs Different browser target, missing font asset, or different viewport/configuration. Compare target, dimensions, static build paths, and font availability. Reproduce with the same target CI uses.
Only the first run has the wrong wrapping The screenshot may be captured before the web font and resulting layout settle. Confirm the font request completes. In custom capture code, await document.fonts.ready; make external font loading deterministic where possible.
CI reports a missing reference The baseline was never committed, the story identity changed, or the reference path differs. Run an intentional local update, inspect the result, and commit the correct references. Keep --requireReference enabled in CI.
Every run has noisy diffs Changing data, animation, assets, timing, or browser environment. Stabilize story inputs and capture configuration. Use Loki’s skip or async completion approach for stories that cannot produce a stable still image.
Loki cannot connect to Storybook or the static build Storybook is not running, the build failed, or the configured URI does not match the project. Start the server or build first; check the URI and framework-specific option against the installed Loki version.
Preload is present but the wrong typeface appears Preloading only fetches an asset; it does not select the CSS family or establish glyph coverage. Verify the stylesheet rule, actual loaded font, and characters supported by that face. Treat preload as a loading hint, not a rendering check.

8. Performance, reliability, and cost considerations

Screenshot work scales with the number of stories, targets, and reference updates. Keep the suite focused on useful visual states, and use representative language content rather than duplicating near-identical stories without a reason. Local font assets avoid dependence on a remote font service during each CI run. Static builds can make the CI input explicit, while pinned dependencies and a consistent browser target reduce environment drift.

Loki is a self-managed test workflow: account for the CI time and storage used by captures and reference images in your own environment. The cited Loki material does not establish a universal run-time benchmark or service price, so actual time and cost depend on your stories, browser target, and CI provider. Review visual changes before updating baselines; automatic baseline replacement weakens the test’s value.

9. Or skip the browser setup

ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. For a quick capture of a rendered page, one GET request returns an image or PDF; it does not replace Loki’s reference comparison workflow for Storybook regressions. The API call below captures a public Storybook URL—adapt it to a URL your environment can access. See the ScreenshotNeo API documentation.

cURL

curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp

Python

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

Node.js

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await require('node:fs/promises').writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo removes cookie banners, popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed; response headers report the page verdict and billing status. Its MCP server lets AI agents take screenshots. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.

FAQ

Does Loki have a documented font-wait option?

The cited Loki documentation does not identify a universal font-wait setting. Use deterministic story assets and, where you control capture code, wait for the browser’s font readiness promise.

Should I use one font stack for every Indian language?

No. Select families for the script and design your interface uses, and verify the characters in your actual content.

Should a font change always update the reference images?

Only when the rendered change is intended. First verify the loaded font and resulting layout, then review and commit the new reference images.

Can ScreenshotNeo replace Loki for regression tests?

ScreenshotNeo can capture a URL as an image or PDF. Loki’s workflow compares captures against references, so use Loki when you need that baseline-and-diff process.