Deep Linking
A deep link is a URL like myapp://invite/abc123 that the OS routes to your app. Tynd handles the platform-specific registration at build time and delivers the URL to your frontend.
Declare the scheme
export default {
runtime: "lite",
backend: "backend/main.ts",
frontendDir: "dist",
protocols: ["myapp"],
bundle: { identifier: "com.example.myapp" },
} satisfies TyndConfig;protocols— array of scheme names (no://).- Reserved schemes (
http,https,file,ftp,mailto,javascript,data,about,blob,tynd,tynd-bin) are rejected at config-validation time.
Registration per OS
tynd build wires each installer format:
- macOS
.app— writesCFBundleURLTypesintoInfo.plist. - Windows NSIS / MSI — creates
HKCU\Software\Classes\<scheme>\shell\open\commandregistry entries (current user — no admin prompt). - Linux
.deb/.rpm/.AppImage— addsMimeType=x-scheme-handler/<scheme>;+%Uin theExec=line of the generated.desktopfile.
Handle URLs at runtime
Pair with singleInstance so duplicate launches forward the URL to the primary:
import { singleInstance } from "@tynd/core/client";
const { acquired } = await singleInstance.acquire("com.example.myapp");
if (!acquired) process.exit(0);
singleInstance.onOpenUrl((url) => {
// url = "myapp://invite/abc123"
const parsed = new URL(url);
router.navigate(parsed.pathname); // navigate to "/invite/abc123"
});onOpenUrl fires both on cold start (argv contains the URL) and on duplicate launch (the primary receives the forwarded URL).
Testing
macOS
After running tynd build --bundle app, open the .app once so Launch Services registers the scheme, then:
open "myapp://test/path"Windows
After running the NSIS installer:
start myapp://test/pathCheck registration with:
Get-ItemProperty "HKCU:\Software\Classes\myapp"Linux
After installing the .deb / .rpm:
xdg-open "myapp://test/path"Check registration:
xdg-mime query default x-scheme-handler/myappDuring development (outside an installer), you can hand-register for testing:
# Create a temporary .desktop file
cat > ~/.local/share/applications/myapp-dev.desktop <<EOF
[Desktop Entry]
Name=MyApp Dev
Exec=/path/to/dev-binary %U
Type=Application
MimeType=x-scheme-handler/myapp;
EOF
update-desktop-database ~/.local/share/applications/Parsing URLs
Use the standard URL API — available in both runtimes:
singleInstance.onOpenUrl((url) => {
const parsed = new URL(url);
// parsed.protocol === "myapp:"
// parsed.hostname === "invite"
// parsed.pathname === "/abc123"
// parsed.searchParams.get("foo")
if (parsed.hostname === "invite") {
acceptInvite(parsed.pathname.slice(1));
}
});Multiple schemes
protocols: ["myapp", "myapp-dev"]Useful if you want a prod + dev scheme that route differently. Both are handled by the same onOpenUrl callback — inspect new URL(url).protocol to branch.
Security
- Treat deep-link input as untrusted. Users and websites can craft arbitrary payloads. Validate path / query against a whitelist before acting.
- Don’t run privileged actions on link open without confirmation.
myapp://delete-allis a foot-gun. - URL encoding — browsers encode before launching.
URLautomatically decodes pathname / searchParams.
File type associations
Not currently exposed as a first-class config field. If you need .myapp files to launch your app:
- macOS — edit the generated
Info.plistpost-build (CFBundleDocumentTypes). - Windows — add registry entries under
HKCU\Software\Classes\.myapp+HKCU\Software\Classes\MyApp.Document\shell\open\command. - Linux — extend the
.desktopfile withMimeType=application/x-myapp;.
A future release may expose a fileAssociations config field; for now, roll your own post-build step.