This skill should be used when working on Valdi framework projects...
Specialized guidance for developing cross-platform applications with Valdi, Snapchat's native UI framework that compiles TypeScript/TSX to native iOS, Android, and macOS views.
Valdi is NOT a WebView-based framework. It compiles TypeScript components directly into native platform code, delivering native performance without JavaScript bridges. The framework has been used in Snap's production apps for over 8 years.
Invoke this skill when:
Valdi uses Bazel exclusively for builds. Never suggest alternative build systems.
Common commands:
# Install CLI globally
pnpm install -g @snap/valdi
# Setup development environment
valdi dev_setup
# Check environment health
valdi doctor
# Bootstrap new project
valdi bootstrap
# Install platform dependencies
valdi install ios
valdi install android
# Enable hot reload during development
valdi hotreload
# Sync project configuration
valdi projectsync
Valdi components are class-based with lifecycle methods:
import { Component, ComponentContext } from 'valdi_core';
interface ViewModel {
title: string;
count: number;
}
class MyComponent extends Component<ViewModel, ComponentContext> {
onCreate(): void {
// Initialize component
console.log('Component created');
}
onMount(): void {
// Component mounted to view hierarchy
}
onUnmount(): void {
// Cleanup before removal
}
onRender() {
return (
<view style={styles.container}>
<label style={styles.title}>{this.viewModel.title}</label>
</view>
);
}
}
Valdi provides native UI elements (NOT HTML):
| Element | Purpose |
|---|---|
<view> |
Container view (like div) |
<layout> |
Flexbox layout container |
<scroll> |
Scrollable container |
<label> |
Text display |
<image> |
Image display |
<video> |
Video player |
<slot> |
Content projection |
Element attributes use native styling, not CSS:
const styles = {
container: {
alignItems: 'center',
justifyContent: 'center',
backgroundColor: '#FFFFFF',
padding: 16,
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#000000',
},
};
Standard Valdi project layout:
my_project/
āāā BUILD.bazel # Main Bazel build file
āāā package.json # Node dependencies (use pnpm)
āāā .eslintrc.js # ESLint configuration
āāā app_assets/ # Application assets
ā āāā images/
āāā src/
ā āāā android/ # Android-specific code
ā āāā cpp/ # C++ native modules
ā āāā ios/ # iOS-specific code
ā āāā valdi/ # Valdi TypeScript/TSX
ā āāā _configs/ # Valdi configs
ā āāā tsconfig.json
ā āāā .terserrc.json
ā āāā my_module/ # Your module
ā āāā BUILD.bazel
ā āāā tsconfig.json
ā āāā res/ # Module resources
ā āāā src/
ā āāā index.ts
ā āāā MyComponent.tsx
āāā standalone_app/ # Standalone app config
Application BUILD.bazel:
load("//bzl:valdi.bzl", "valdi_application", "valdi_exported_library")
valdi_application(
name = "my_app",
title = "My Valdi App",
version = "1.0.0",
ios_bundle_id = "com.example.myapp",
ios_device_families = ["iphone"],
android_theme = "Theme.MyApp.Launch",
android_app_icon = "app_icon",
root_component = "App@my_module/src/MyApp",
assets = glob(["app_assets/**/*"]),
deps = ["//path/to/src/valdi/my_module"],
)
valdi_exported_library(
name = "my_app_export",
ios_bundle_id = "com.example.myapp.lib",
bundle_name = "MyApp",
deps = ["//path/to/src/valdi/my_module"],
)
Module BUILD.bazel:
load("//bzl:valdi.bzl", "valdi_module")
valdi_module(
name = "my_module",
srcs = glob(["src/**/*.ts", "src/**/*.tsx"]) + ["tsconfig.json"],
assets = glob(["res/**/*.{jpeg,jpg,png,svg,webp}"]),
android = struct(
class_path = "com.example.valdi.modules.my_module",
native_deps = ["//path/to/android:native_module"],
release = True,
),
ios = struct(
module_name = "SCCMyModule",
native_deps = ["//path/to/ios:native_module"],
release = True,
),
native = struct(
deps = ["//path/to/cpp:native_module_cpp"],
),
deps = [
"//src/valdi_modules/valdi_core",
"//src/valdi_modules/valdi_tsx",
],
visibility = ["//visibility:public"],
)
Valdi supports type-safe bindings to native code:
CppModule.d.ts:
declare module 'CppModule' {
export function performCalculation(value: number): number;
export function getNativeString(): string;
}
NativeModule.d.ts:
declare module 'NativeModule' {
export function showNativeAlert(message: string): void;
export function getPlatformInfo(): { os: string; version: string };
}
Usage in component:
import * as CppModule from 'CppModule';
import * as NativeModule from 'NativeModule';
class MyComponent extends Component<ViewModel, ComponentContext> {
onMount() {
const result = CppModule.performCalculation(42);
const platform = NativeModule.getPlatformInfo();
}
}
All changes must work across iOS, Android, and macOS:
Performance is critical:
Initial setup:
# Install Valdi CLI
pnpm install -g @snap/valdi
# Setup development environment (takes 10-20 minutes first time)
valdi dev_setup
# Verify installation
valdi doctor
Creating a new project:
mkdir my_project && cd my_project
valdi bootstrap
valdi install ios # or android
Development cycle:
# Start hot reload for live updates
valdi hotreload
# After changing dependencies or resources
valdi projectsync
Editor setup (VSCode/Cursor):
valdi-vivaldi.vsix (device logs, language support)valdi-debug.vsix (JavaScript debugger)Problem: Treating Valdi like React or web development.
// WRONG - HTML elements don't exist
<div className="container">
<span>Hello</span>
</div>
// CORRECT - Use Valdi native elements
<view style={styles.container}>
<label>Hello</label>
</view>
Problem: Using CSS syntax for styles.
// WRONG - CSS syntax
const styles = {
container: {
'background-color': '#fff',
'font-size': '16px',
}
};
// CORRECT - Camel case, numeric values
const styles = {
container: {
backgroundColor: '#FFFFFF',
fontSize: 16,
}
};
Problem: Editing Djinni-generated native bindings directly.
Solution: Always modify source files, never generated code. Regenerate bindings after source changes.
Problem: Using npm/yarn scripts, webpack, or other bundlers.
Solution: Valdi uses Bazel exclusively. Use valdi CLI commands.
Problem: Only testing on one platform.
Solution: Always verify changes work on iOS, Android, and macOS where applicable.
Standard Valdi ESLint setup:
// .eslintrc.js
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: './tsconfig.json',
ecmaVersion: 2020,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
},
plugins: [
'@typescript-eslint',
'eslint-plugin-valdi',
'rxjs',
'import',
'unused-imports',
],
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
],
rules: {
// Valdi-specific rules
},
};
Standard tsconfig.json for Valdi modules:
{
"extends": "../../_configs/tsconfig.json",
"compilerOptions": {
"jsx": "react",
"jsxFactory": "Valdi.createElement",
"strict": true,
"moduleResolution": "node",
"esModuleInterop": true
},
"include": ["src/**/*"]
}
Valdi provides these core modules:
| Module | Purpose |
|---|---|
valdi_core |
Core component system, lifecycle |
valdi_tsx |
TSX/JSX support |
valdi_protobuf |
Protobuf serialization |
valdi_http |
HTTP client |
valdi_storage |
Persistent encrypted storage |
valdi_navigation |
Navigation system |
valdi_rxjs |
RxJS integration |
Using Hermes Debugger:
Using Valdi Inspector:
| Command | Purpose |
|---|---|
valdi dev_setup |
Setup development environment |
valdi doctor |
Check environment health |
valdi bootstrap |
Create new project |
valdi install [platform] |
Install platform dependencies |
valdi hotreload |
Enable live updates |
valdi projectsync |
Sync project configuration |
| Method | When Called |
|---|---|
onCreate() |
Component initialization |
onMount() |
Added to view hierarchy |
onUnmount() |
Before removal from hierarchy |
onRender() |
Render component UI |
| Element | HTML Equivalent |
|---|---|
<view> |
<div> |
<layout> |
Flexbox container |
<scroll> |
Scrollable div |
<label> |
<span> / <p> |
<image> |
<img> |
<video> |
<video> |
For more details:
references/component-patterns.md - Advanced component patternsreferences/bazel-configuration.md - Detailed Bazel setupreferences/native-bindings.md - Native code integrationRemember: Valdi compiles to native code - think native, not web. Use Bazel for builds, pnpm for Node dependencies, and test on all target platforms.