@linktr.ee/linkapp
v0.0.48
Published
Development, build, and deployment tooling for LinkApps
Downloads
1,714
Maintainers
Keywords
Readme
@linktr.ee/linkapp
Development, build, and deployment tooling for LinkApps - custom interactive link experiences for Linktree profiles.
Installation
npm install @linktr.ee/linkappOr create a new LinkApp project:
npm create @linktr.ee/linkapp@latestCommands
Development
Start the development server with hot reloading and theme preview UI:
linkapp dev [--port 3000]The dev server provides:
- Live preview of all layouts (expanded, featured, carousel)
- Theme switcher with Linktree presets
- Settings editor for testing configurations
- Hot module replacement
Building
Build your LinkApp for production:
linkapp build [options]Options:
--sourcemap- Generate source maps for debugging--profile- Enable bundle profiling--compress- Generate Brotli-compressed files (30-40% smaller)
Outputs to dist/ directory with:
- Content-hashed filenames for cache busting
build-manifest.jsonwith asset versions- Optimized React vendor chunks for better caching
Deployment
Deploy your LinkApp to Linktree:
linkapp deploy [--qa] [--skip-confirm]Options:
--qa- Deploy to QA environment instead of production--skip-confirm- Skip confirmation prompt
The deployment process:
- Validates project structure and configuration
- Builds if needed (runs
linkapp build) - Generates manifest files from
linkapp.config.ts - Packs project into tarball
- Uploads to Linktree API
- Returns build ID and admin URL
Authentication
Authenticate with Linktree using OAuth device flow:
linkapp login [--qa] # Login
linkapp logout [--qa] # LogoutTokens are stored in ~/.config/linkapp/auth-token.json.
Components
Add UI components from the registry:
linkapp add <component>Example: linkapp add button
URL Testing
Test URL matching rules from your config:
linkapp test-url-match-rules <url>Example: linkapp test-url-match-rules https://google.com/search
Exports
CLI
{
"bin": {
"linkapp": "bin/cli.js"
}
}Types
import type { AppProps, LinkAppConfig, SettingsElement } from '@linktr.ee/linkapp/types'
// Layout component props
interface MySettings {
title: string
color: string
}
export default function ExpandedLayout({ settings, theme }: AppProps<MySettings>) {
return <div>{settings.title}</div>
}SDK
import { useExpandLinkApp } from '@linktr.ee/linkapp/sdk'
// In featured layout, trigger popup/modal
function FeaturedLayout() {
const expandLinkApp = useExpandLinkApp()
return (
<button onClick={() => expandLinkApp({ itemId: '123' })}>
Expand
</button>
)
}Project Structure
A LinkApp project has this structure:
my-linkapp/
├── app/
│ ├── expanded.tsx # Required: default layout
│ ├── featured.tsx # Optional: featured layout
│ ├── featured-carousel.tsx # Optional: carousel variant
│ ├── layout.tsx # Optional: wrapper component
│ ├── globals.css # Global styles
│ └── icon.svg # LinkApp icon
├── components/ # Your components
├── public/ # Static assets (copied to dist/)
├── linkapp.config.ts # Configuration
├── postcss.config.mjs # PostCSS config (Tailwind v4)
├── components.json # Component registry config
└── package.jsonConfiguration
The linkapp.config.ts file defines your LinkApp:
import { defineConfig } from '@linktr.ee/linkapp/types'
export default defineConfig({
manifest: {
name: 'My LinkApp',
tagline: 'A custom link experience',
category: 'share',
author: {
name: 'Your Name',
website: 'https://example.com'
},
supporting_links: {
terms_of_service: 'https://example.com/terms',
privacy_policy: 'https://example.com/privacy'
}
},
settings: {
title: 'My LinkApp Settings',
elements: []
}
})Important: The manifest.name becomes your LinkApp ID (kebab-cased) and cannot be changed after first deployment.
See the Configuration Reference for all available options.
Build System
Uses Rsbuild (Rspack) for fast, optimized builds:
- Entry Point:
.linkapp/main.tsx(auto-generated) - Alias:
@/points to project root - PostCSS: Supports Tailwind CSS v4
- Asset Prefix:
./for S3 subdirectory deployment - Code Splitting: React vendor chunks separated for caching
- Compression: Optional Brotli compression with
--compressflag
Layout Components
Expanded Layout (Required)
Compact layout for profile pages:
import type { AppProps } from '@linktr.ee/linkapp/types'
interface Settings {
title: string
}
export default function Expanded({ settings, theme, __linkUrl }: AppProps<Settings>) {
return (
<div style={{ color: theme.textColor }}>
<h1>{settings.title}</h1>
<a href={__linkUrl}>Visit Link</a>
</div>
)
}Featured Layout (Optional)
Hero-style layout for featured content:
import { useExpandLinkApp } from '@linktr.ee/linkapp/sdk'
import type { AppProps } from '@linktr.ee/linkapp/types'
export default function Featured({ settings }: AppProps<Settings>) {
const expand = useExpandLinkApp()
return (
<div onClick={() => expand()}>
<h1>{settings.title}</h1>
</div>
)
}The featured layout can trigger a modal that displays the expanded layout.
Theme System
LinkApps receive theme data via props:
interface Theme {
// Legacy properties (maintained for compatibility)
textColor: string
backgroundColor: string
borderRadius: number
// Modern CSS variables (preferred)
cssVariables: Record<string, string>
}Apply CSS variables for theming:
/* app/globals.css */
:root {
--color-primary: var(--theme-primary, #000);
--color-background: var(--theme-background, #fff);
}PostMessage API
LinkApps run in iframes and communicate with the parent window:
import { useExpandLinkApp } from '@linktr.ee/linkapp/sdk'
function MyComponent() {
const expand = useExpandLinkApp()
return (
<button onClick={() => expand()}>
Open in Modal
</button>
)
}Development
Local Development
npm run build # Compile TypeScript
npm run dev # Watch mode
npm run test # Run tests
npm run lint # Lint with Biome
npm run format # Format with BiomeTesting Locally
Link the package globally to test CLI changes:
cd packages/linkapp
npm run build
npm link
cd ../../apps/my-test-app
linkapp devPackage Scripts
build- Compile TypeScript todist/dev- Watch mode compilationtest- Run Vitest teststest:watch- Watch mode for testslint- Lint with Biomeformat- Format with Biomeclean- Remove build artifacts
Key Concepts
LinkApp ID
The LinkApp ID is derived from manifest.name using kebab-case:
"My Cool App"→"my-cool-app"- This ID is permanent and identifies your LinkApp across deployments
- Cannot be changed after first deployment
Layout Detection
The supports_featured_layout setting is auto-detected based on the presence of app/featured.tsx. You don't need to manually configure it.
Settings Schema
Settings are defined as an array of elements:
elements: [
{
type: 'text',
id: 'title',
label: 'Title',
defaultValue: 'Hello'
},
{
type: 'select',
id: 'style',
label: 'Style',
options: ['minimal', 'bold'],
defaultValue: 'minimal'
},
{
type: 'array',
id: 'items',
label: 'Items',
element: {
type: 'text',
id: 'name',
label: 'Name'
}
}
]Supported types: text, select, file, switch, array, number, date, color
Requirements
- Node.js >= 18.0.0
- npm >= 10.0.0
Related Packages
@linktr.ee/create-linkapp- Scaffolding tool for new projects@linktr.ee/registry- Component registry
License
See LICENSE file in repository root.
