- Installs
- 525
- Last 24h
- +51
- Trending rank
- #3317
- Security
- Not audited
Install
Works with every agent the skills CLI supports.
npx skills add https://github.com/margelo/react-native-skills --skill kotlin- 01
Install
Run the command in your project. The CLI asks which agents to install for.
- 02
Describe the task
Ask your agent for something this skill covers. It loads kotlin automatically when relevant.
- 03
Review the result
The agent follows the SKILL.md steps, scripts and references bundled with the skill.
SKILL.md
The instructions your agent reads when the skill is triggered.
Kotlin
Use this skill for Kotlin code that needs strong type modeling, explicit async boundaries, and idiomatic Android integration. When the code is part of a Nitro Module, pair this with build-nitro-modules for generated specs, Promise mapping, annotations, and HybridObject constraints.
Workflow
- Read the local Kotlin code, generated abstract classes, and public API shape before editing.
- Model valid states with Kotlin types before implementing behavior.
- Choose whether work is synchronous, coroutine-based, or explicitly dispatched.
- Keep properties cheap and nonblocking.
- Make thread hops, Android service access, I/O, and fallible work visible in the API.
Type-Safe API Design
- Represent state variants with sealed interfaces/classes, normal interfaces, or concrete classes. Do not encode variants as one data class with many nullable fields.
- Keep related values non-null on the variant where they are valid. If
barcodeandbarcodeTypemust exist together, put them onScannedBarcode. - Use nullable types for real absence inside one state, not for "maybe this object is a different kind of thing".
- Prefer compile-time narrowing over caller-side null probing. Repeated null checks for related fields usually mean the model is too loose.
// Avoid: related fields are nullable and the valid combinations are implicit.
data class ScannedData(
val position: Point,
val text: String?,
val barcode: String?,
val barcodeType: BarcodeType?,
val face: Rect?,
)
// Prefer: each variant exposes only the fields that are valid for it.
sealed interface ScannedData {
val position: Point
}
data class ScannedText(
override val position: Point,
val text: String,
) : ScannedData
data class ScannedBarcode(
override val position: Point,
val barcode: String,
val barcodeType: BarcodeType,
) : ScannedData
data class ScannedFace(
override val position: Point,
val face: Rect,
) : ScannedData
Async and Threading
- Use coroutines for naturally suspending APIs and Android I/O that already has coroutine support.
- Keep quick, deterministic, local work synchronous. Do not introduce coroutines, dispatchers, or Promise plumbing for simple value construction, cached metadata, or pure transforms.
- Use explicit owned dispatchers/scopes or Nitro
Promise.parallelfor CPU-bound synchronous work that should not run on the caller thread. - Avoid the main dispatcher unless the Android API requires it, such as view/UI mutation or lifecycle APIs. Keep main-thread blocks small and move parsing, conversion, I/O, session negotiation, and CPU work to an owned dispatcher or async API.
- Do not use
runBlockingin library code, property getters, setters, or JS-facing entry points. - Do not hide a thread hop, service lookup, blocking I/O, permission flow, or fallible native operation behind a property.
- If a value is emitted by a specific Android callback/thread, prefer a listener, Flow, or event API. In Nitro specs, expose a listener or a
Promise<T>method. - Treat repeated
launch,withContext, dispatcher, handler, or executor hops as an architecture smell. A component should either own the coroutine scope/dispatcher/lifecycle it works on, or cross into that owner once at the public async boundary or native callback boundary. - If a workflow bounces between main, IO, default, native, and JS/Nitro contexts in multiple nested places, stop and redesign the object/lifecycle/API. Excessive hops hide latency, make ordering harder to reason about, and create future performance problems.
- Do not fix races or readiness bugs with
delay,Thread.sleep,Handler.postDelayed, timers, extra dispatcher hops, or calling the same method twice. Fix the owner dispatcher/lifecycle, state machine, callback/event, Flow, or async API boundary instead. Retry only for external nondeterminism such as hardware, OS services, remote services, or network, and keep retries bounded, cancellable, and idempotent. - Keep mutable shared state owned by one coroutine scope, dispatcher, lock, or Android component lifecycle. Avoid mixing ownership models without a clear boundary.
- Treat
Mutex,synchronized, andReentrantLockas last-resort synchronization, not default listener or callback plumbing. Before adding one, identify the exact shared mutable values, concurrent callers, and why a single owner dispatcher/lifecycle, channel/Flow, or immutable snapshot is not enough. - Never hold a lock while invoking callbacks, calling into JS/Nitro, or calling unknown user code. Snapshot listeners under the lock if needed, unlock, then call them. A listener removed during an in-flight emission may receive that current event.
Properties
- Use
valandvarfor cheap in-memory state or derived values. - Use methods for side effects, I/O, Android service calls, permission checks, allocation, blocking work, or operations that can fail.
- If a property would need to wait for another thread, redesign it as an async method or event.
// Avoid: blocking work hidden in a getter.
val status: SessionStatus
get() = runBlocking { session.status() }
// Prefer: make the async boundary explicit.
fun getStatus(): Promise<SessionStatus> {
return Promise.async {
session.status()
}
}
Kotlin Style
- Make classes final by default. Kotlin classes already are final unless marked
open; keep them that way unless inheritance is intentional. - Prefer
data classfor plain values and regular classes for owned resources or lifecycle. - Prefer
sealed interfaceorsealed classfor closed result families. - Use
require,check, or specific exceptions for invalid inputs and invalid state. Do not silently no-op user-reachable failures. - Keep Java interop explicit. Convert platform types into Kotlin types before exposing them through the public API where practical.
- Avoid
!!except at narrow boundaries where a prior check makes the invariant obvious. Prefer early returns,requireNotNull, or typed state. - Prefer explicit
returnstatements inside multi-line control flow. Do not lift the return outside a multi-linetry/catch,if,when, or lambda just to make it expression-like; usetry { return value } catch (...) { return fallback }. - In multi-line lambdas, use labeled returns such as
return@map valuefor the result. Omit the label only for true single-expression lambdas likeitems.map { it.toString() }; do not writeitems.map { return@map it.toString() }. - Prefer inline shorthand for unambiguous single-expression lambdas:
formats.map { it.toMLKitFormat() }, not a multi-linemap { format -> format.toMLKitFormat() }. Do not useitwhen a surrounding or nested lambda already usesit; name parameters in nested lambdas or when clarity needs it. - Treat a filename as a scope contract.
HybridDataScanner.ktshould implementHybridDataScanner; it should not also contain Android helpers, geometry conversions, listeners, or extension utilities. - Keep one top-level implementation type per file. Do not put secondary classes, interfaces, enums, option adapters, coordinators, delegates, or helper types below the primary type. If a helper deserves a type, it deserves its own file.
- Keep one focused extension/conversion per file. Do not create a catch-all extension file just because every helper is an extension. Split conversions into named files such as
Barcode+toScannedCode.kt,TargetBarcodeFormat+toMLKitFormat.kt, andBarcodeFormat+fromMLKitBarcodeFormat.ktwithinternalvisibility where appropriate. - Never put extension
fun,val, orvardeclarations insideHybrid*implementation files or other primary implementation files, even when they are private, tiny, or only used by that file. Put every extension in a separate namedType+operation.ktextension/converter file so code splitting, maintainability, and future diffs stay clean. - Do not place private top-level extension properties/functions below a primary class just because Kotlin allows it. Companion extensions, enum/platform mappings, barcode format conversions, and ML Kit/CameraX adapters belong in their own named files.
- Use private top-level helpers in the same file only when they are tiny, non-extension functions/properties, and exist solely to support that file's primary type.
- Extract Android preflight checks and platform boilerplate out of
Hybrid*factories and implementation methods. Permission checks, manifest feature/permission validation,PackageManagercapability checks, service availability checks, and similar setup guards belong in focused helper files; the HybridObject call site should stay one or two lines. - Do not create broad
Utils.ktfiles for these helpers. Name the file after the platform type or domain check and keep each helper focused on one behavior. - Use line count as a review signal: files below roughly 300 lines are usually fine only after the one-type-per-file and no-helpers-in-factory rules are satisfied. Size caused by helpers, conversions, or Android glue is a design issue.
- Put conversions on the element type, not on
Listor array types, when the conversion only reads one element. PreferTargetBarcodeFormat.toMLKitFormat()plusformats.map { it.toMLKitFormat() }at the call site overArray<TargetBarcodeFormat>.toMLKitFormats(). - Do not put domain conversions on broad/common receivers such as
Int,String,Double, orAny, even privately. Prefer the domain type's companion/factory direction, such asBarcodeFormat.Companion.fromFormat(format: Int), overInt.toBarcodeFormat(). - Add collection extension functions only when the collection has real domain behavior, such as validation across elements, deduplication, ordering, batching, caching, nonempty checks, or error aggregation. If the receiver is a concrete
Array<SomeDomainType>orList<SomeDomainType>and the body is mostlymap { ... }, keep it in caller code. - When a Kotlin converter returns or accepts Android platform
Intconstants, apply the matching AndroidX/Java/Kotlin annotation where the API supports it, such as a CameraX@ImageCapture.FlashMode-style annotation on the function, return value, or parameter. Check the annotation target and retention before placing it; do not annotate blindly when the platform API exposes only a plainInt. - When converting from an annotated platform
Int, put the annotation on theformat: Intparameter if the annotation target supports parameters. For example, preferBarcodeFormat.Companion.fromFormat(@SomeBarcodeFormat format: Int)over an untyped primitive input.
Nitro Notes
- Kotlin HybridObject implementations must extend the generated
Hybrid*Specclass and include@Keepplus@DoNotStrip. - Use
Promise.asyncfor suspending or I/O work andPromise.parallelfor CPU-bound synchronous work. - Avoid manually creating or passing around
Promise<T>instances. PreferPromise.async,Promise.parallel,Promise.resolved, andPromise.rejected; use a manual Promise only for real native completion/listener/callback bridges that cannot be wrapped as suspend APIs. - For general callback APIs such as Google
Task<T>, first write a generic suspend adapter in its own extension file, such asTask+await.kt, then call it fromPromise.async { task.await() }. Do not hand-wireaddOnSuccessListener/addOnFailureListener/addOnCanceledListenerinto public HybridObject methods. - Use
Promise<Unit>forPromise<void>. - Access
NitroModules.applicationContextlazily and fail explicitly if it is unavailable. - Generated properties are synchronous JS entry points. Redesign them as methods or listeners if they need Android thread affinity or async work.
FAQ
What does the kotlin skill do?
Design, implement, and review Kotlin and Android APIs. Use when working on .kt files, Kotlin nullability, sealed classes/interfaces, coroutines, Android threading, Java interop, or Kotlin-backed React Native Nitro Module implementations.
How do I install kotlin?
Run npx skills add https://github.com/margelo/react-native-skills --skill kotlin in your project. The skills CLI detects the agents you use and copies the skill into the right folder. For Claude Code you can also place the skill folder at ~/.claude/skills/kotlin/ (personal) or .claude/skills/kotlin/ (project).
Which AI agents can use kotlin?
Any agent that supports the open Agent Skills format (a folder with a SKILL.md file), including Claude Code, Codex, Cursor, GitHub Copilot, Gemini CLI, Windsurf, Cline, OpenCode and more.
Is kotlin safe to install?
No partner security audit is available yet. Skills can run commands and scripts, so read the SKILL.md and any bundled files before installing.
Who maintains kotlin?
It is published by margelo in margelo/react-native-skills. It has 525 recorded installs.
More from margelo/react-native-skills
react-native-mmkv
margelo/react-native-skills
Fast synchronous key-value storage for React Native via react-native-mmkv (Nitro-backed). Covers creating and configuring MMKV instances, reading/writing all value types (string, number, boolean, buffer), React hooks for reactive UI, value-change listeners, encryption, AsyncStorage migration, and state-management integrations (zustand, redux-persist, jotai, react-query). Also covers storage limits, multi-process mode, and the V4 upgrade from the class-based API.
build-nitro-modules
margelo/react-native-skills
Builds and designs React Native Nitro Modules with Nitrogen, HybridObject TypeScript specs, Nitro View components, generated native implementations, zero-copy and native-state APIs, Swift/Kotlin/C++ bindings, example apps, and testing. Use when creating a Nitro Module, adding or reviewing HybridObjects, building a Nitro View (HybridView) component, designing Nitro-specific public APIs, implementing native functionality, or setting up the nitrogen codegen pipeline. Pair with api-design for general library API shape.
react-native-vision-camera
margelo/react-native-skills
Best-practices guide for React Native VisionCamera v5 setup, migration, capture, controls, outputs, and basic frame processing. Use the separate react-native-vision-camera-realtime skill for production low-latency GPU, ML, CV, Skia or WebGPU pipelines and frame-coupled overlays.
nitro-fetch
margelo/react-native-skills
Fast native networking primitives for React Native built on Nitro Modules — react-native-nitro-fetch, react-native-nitro-websockets, and react-native-nitro-text-decoder. Covers the fetch API, global replacement, prefetching and cold-start cache warming, the NitroWebSocket class and pre-warming, migrating from React Native's built-in WebSocket, the in-process NetworkInspector, native Perfetto / Instruments tracing, the native TextDecoder, and plugging nitro-fetch into axios via a custom adapter.
api-design
margelo/react-native-skills
Design and review predictable public APIs for TypeScript, JavaScript, React, and React Native libraries. Use when shaping exported functions, classes, hooks, options objects, event and listener APIs, error behavior, naming, cross-platform abstractions, or JS-only packages. Pair with build-nitro-modules when the library is backed by Nitro.
swift
margelo/react-native-skills
Design, implement, and review Swift APIs and Apple-platform code. Use when working on .swift files, Swift types, Foundation or AVFoundation APIs, DispatchQueue, async/await, Task, actors, MainActor, thread-affine state, or Swift-backed React Native Nitro Module implementations.
Popular mobile skills
design-mobile-apps
designed-by-ai/skills
Design mobile app screens with Sleek, edit Sleek projects, and implement their designs in React Native or HTML.
ios-design
uizze.sh
Design and review iOS UI/UX for iPhone apps in SwiftUI, UIKit, or React Native. Use for mobile app design, screen redesigns, navigation flows, typography, forms, keyboard and safe-area layout, and accessibility checks. Apply Apple Human Interface Guidelines and consult real product references through the optional Uizze connection.
vercel-react-native-skills
vercel-labs/agent-skills
React Native and Expo best practices for building performant mobile apps. Use when building React Native components, optimizing list performance, implementing animations, or working with native modules. Triggers on tasks involving React Native, Expo, mobile performance, or native platform APIs.
animate-expo
emilkowalski/skills
Build animations in React Native and Expo, making the decisions in the order that determines whether they feel right — should it animate, which thread it runs on, which properties, spring or timing, how the gesture hands off, how it degrades. Writes the implementation with Reanimated, Gesture Handler, Expo Router and expo-haptics. Use when animating anything in an Expo app, adding gestures, sheets, screen transitions, press feedback or haptics, or fixing motion that stutters on device. For web animation use animate.
xcode-project-setup
firebase/agent-skills
Safely modifies Xcode projects (.pbxproj) to add Swift Packages and link files. Use this skill whenever an iOS project needs dependencies installed (e.g. Firebase, Alamofire).
firebase-remote-config-basics
firebase/agent-skills
Manages Firebase Remote Config templates, feature flags, loading strategies, and SDKs (Android, iOS). Use when downloading/deploying remoteconfig JSON templates, managing version history/feature flags, setting in-app defaults, fetchAndActivate(), real-time listeners, or SDK setup. Don't use for Firebase Hosting, Auth, Firestore, Data Connect, Crashlytics, or App Hosting.
