@straitlink/web
v0.7.0
Published
Deep linking for the web: deferred match on first visit and click attribution, in a few lines.
Maintainers
Readme
@straitlink/web
Deep linking for the web: deferred match + attribution, in a few lines.
The device signature is kept in lockstep with the server and the other app SDKs via shared golden vectors (this package runs the same vectors in CI), so a change that would make them drift fails the tests.
Quick start (60 seconds)
1. Install.
npm install @straitlink/web2. Ask once, on page load. The publishable key (st_pub_live_…) is in Dashboard → Get started and is safe in web pages.
import { resolveDeferredLink } from '@straitlink/web';
const result = await resolveDeferredLink({ publishableKey: 'st_pub_live_…', endpoint: 'https://<your-handle>.strait.link' });
console.log(result);You should see: { matched: false, matchMethod: 'none', … } in the console, and one POST /v1/match answering 200 in DevTools → Network. A browser that tapped one of your links shortly before gets matched: true and the link's longUrl.
Platform features
| Feature | Status | Notes |
|---|---|---|
| Web links open the right page | ● Live | Handled by Strait; nothing to install. |
| Deferred match on first visit (resolveDeferredLink) | ◐ Beta | At most once per browser. Never throws. |
| Link opens on your website (reportWebOpen) | ◐ Beta | Counts a page view as an open only when the URL carries strait_click (a landing from a Strait tap), joined to that tap. Needs the engine release that adds the tap id to web landings (on by default; turn it off per workspace or per link). |
| Smart app banner for mobile web (banner) | ◐ Beta | Styled with the Strait design system, light and dark. No requests of its own (apart from loading your icon), no cookies. Docs |
| iPhone install matching | Opt-in | Off until you choose it; proven on iPhone simulators, the real-iPhone run waits on our App Store listing. Runs in your app through the native SDKs, not in this package. Your choice in Dashboard → Settings → iPhone installs: device matching, paste handoff, both or neither, read from Strait at runtime. Device matching is off by default. iPhone matches are labelled Estimated until measured. |
| Published on npm | ● Live | npm install @straitlink/web |
● Live · ◐ Beta · ○ Planned · – Not yet · Opt-in: off until you switch it on. The same words as the platform status page.
Install
npm install @straitlink/webUse
On first launch, ask Strait whether this device recently clicked one of your links — and route to the deferred destination:
import { resolveDeferredLink } from '@straitlink/web';
const result = await resolveDeferredLink({
publishableKey: 'st_pub_live_…', // Dashboard → Get started (safe in apps; never the secret key)
endpoint: 'https://<your-handle>.strait.link', // your workspace's link domain
});
if (result.matched && result.longUrl) {
router.navigate(result.longUrl); // land the user on the right screen
}resolveDeferredLink runs at most once per browser (it records one install
server-side, and remembers that in localStorage; where storage is blocked it can
run again on a later visit). In a browser it doesn't throw: on any network or
server error it resolves to { matched: false, matchMethod: 'none' }, so it's safe
to await on startup. A network error or non-2xx reply (say a 503 while the server
wakes up) doesn't count as the one run: the next visit asks again. Outside a browser (server rendering), pass device or don't
call it, because it reads window.screen.
When the matched tap carried a referral code, the result also has referralCode
(preview, not switched on yet; contract B21). It is absent otherwise.
Count opens on your website
When Strait sends a tap to your website it adds the tap id to the URL
(?strait_click=<tap id>, next to your own parameters such as UTMs). Call reportWebOpen
on page load and that landing is recorded as a link open, joined to the tap, in your dashboard:
import { reportWebOpen } from '@straitlink/web';
const open = await reportWebOpen({ publishableKey: 'st_pub_live_…', endpoint: 'https://<your-handle>.strait.link' });
// open.clickId: the tap this visit came from (pass it as clickId with conversion events)A visit without strait_click (typed URL, reload of a plain page, internal navigation) reports
nothing. A reload or a shared copy of a landing URL is counted once (the open id comes from the
tap id). It never throws. If your site rejects unknown query parameters, turn the tap id off for
the workspace or for one link (webTapId: false on the link API).
What it sends
The SDK collects coarse device fields (screen width, pixel ratio, the browser's
language tag such as en-US, timezone) and posts them to /v1/match; the server
uses only the first two letters of the language. The server adds the
IP it observes and computes the match signature — the client never sees or sends
an IP, and the signature is never used as a cross-app identity.
Smart app banner (mobile web)
Show an "Open in app" bar to visitors on phones. The button is a plain link to your Strait link, so the tap opens your app on the right screen, or the store with the destination kept through the install.
import { Strait } from '@straitlink/web';
Strait.banner({
link: 'https://<your-handle>.strait.link/app', // a Strait link (opened as is)
title: 'Hilltop Shoes',
subtitle: 'Track your order in the app',
icon: '/icons/app-192.png',
theme: 'auto', // 'light' | 'dark' | 'auto'
});It shows only on phones and tablets, never inside your own app (pass inApp or
appUserAgent), and stays hidden for 30 days after the visitor closes it
(dismissDays). It makes no requests of its own (apart from loading your icon)
and sets no cookies; closing it stores the time in localStorage. The only thing
recorded is the tap on the link. dist/banner.js has no imports, so you can also copy it
next to your pages and load it with <script type="module">.
How it looks
The banner follows the Strait design system (v5) in both themes: a warm white bar on light pages and a
warm near-black bar (#191613) on dark ones, one orange button (#FF6A13 with ink text, 6.8:1), a
2 px focus ring, 44 px tap targets on touch screens and no motion when the visitor asks for reduced
motion. theme: 'auto' follows the visitor's light / dark setting.
To match your own brand, pass accent (your button colour). The text on it is picked for contrast
(ink or white) unless you pass accentText.
Preview every option locally: npm run build, serve the repo root, open examples/banner.html
(add ?theme=dark, ?position=bottom or ?accent=%234A2C1D).
| Light | Dark |
|---|---|
|
|
|
API
| export | purpose |
|--------|---------|
| resolveDeferredLink(config, opts?) | the one call you need on launch |
| reportWebOpen(config, opts?) | on page load: records a landing from a Strait tap (strait_click in the URL) as an open; { reported, clickId? } |
| banner(options) / Strait.banner(options) | smart app banner for mobile web; returns { element, dismiss, remove } or null when not shown |
| computeSignature(inputs) | the canonical signature (advanced/testing) |
| h32(s) | the canonical hash (advanced/testing) |
Size and speed
Measured 25 Oct 2026 on 0.6.3.
| What | Size or time | How it was measured |
|---|---|---|
| npm package | 12.5 KB download, 33 KB unpacked, no dependencies | npm pack --dry-run |
| In your bundle, resolveDeferredLink only | 1.4 KB minified, 0.8 KB gzip | esbuild 0.25 --bundle --minify, tree-shaken |
| In your bundle, reportWebOpen only (Unreleased) | 1.4 KB minified, 0.8 KB gzip | same, measured on this branch |
| In your bundle, banner only | 5.1 KB minified, 2.6 KB gzip | same |
| Everything | 7.5 KB minified, 3.7 KB gzip (8.7 KB / 4.1 KB with reportWebOpen) | same, whole entry |
| Start-up | 1.5 ms to load the module, no set-up call | fresh Node 26 process on an M-series Mac, median of 25 |
Example app
examples/web (Vite) routes on the deep link, resolves the deferred link and logs a purchase event, built against this repo's source (CI builds it on every PR). examples/banner.html previews the smart banner.
npm cihere, thencd examples/web && npm ci.cp .env.example .envand set your publishable key (st_pub_…).npm run dev, then openhttp://localhost:5173/product/42.
Docs and support
- Docs: straitlink.in/docs/sdks/web/ · platform status · troubleshooting
- Talk to the Strait team: [email protected] (replies within 1 working day, IST) or call +91 81218 61890.
- Bugs and feature requests: open an issue on this repo.
- Security: never in a public issue. Write to [email protected] (see SECURITY.md).
