View Components
Nitro provides first-class support for creating React Native Views.
Such views can be rendered within React Native apps using Fabric, and are backed by a C++ ShadowNode. The key difference to a Fabric view is that it uses Nitro for prop parsing, which is more lightweight, performant and flexible.
Nitro Views require react-native 0.78.0 or higher, and require the new architecture.
Create a Nitro View
1. Declaration
To create a new Nitro View, declare it's props and methods in a *.nitro.ts file, and export your HybridView type:
import type { HybridView, HybridViewProps, HybridViewMethods } from 'react-native-nitro-modules'
export interface CameraProps extends HybridViewProps {
  enableFlash: boolean
}
export interface CameraMethods extends HybridViewMethods { }
export type CameraView = HybridView<CameraProps, CameraMethods>
2. Code Generation
Then, run nitrogen:
- npm
 - yarn
 - pnpm
 - bun
 
npx nitro-codegen
yarn nitro-codegen
pnpm nitro-codegen
bun nitro-codegen
This will create a C++ ShadowNode, with an iOS (Swift) and Android (Kotlin) interface, just like any other Hybrid Object.
Additionally, a view config (CameraViewConfig.json) will be generated - this is required by Fabric.
3. Implementation
Now it's time to implement the View - simply create a new Swift/Kotlin class/file, extend from HybridCameraViewSpec and implement your .enableFlash property, as well as the common .view accessor:
- Swift
 - Kotlin
 
class HybridCameraView : HybridCameraViewSpec {
  // Props
  var enableFlash: Bool = false
  // View
  var view: UIView = UIView()
}
class HybridCameraView : HybridCameraViewSpec() {
  // Props
  override var enableFlash: Boolean = false
  // View
  override val view: View = View(NitroModules.applicationContext)
}
4. Autolink
Just like any other Hybrid Object, add the Hybrid View to your nitro.json's autolinking configuration:
{
  // ...
  "autolinking": {
    "CameraView": {
      "swift": "HybridCameraView",
      "kotlin": "HybridCameraView"
    }
  }
}
Now run nitrogen again.
5. Initialization
Then, to use the view in JavaScript, use getHostComponent(..):
import { getHostComponent } from 'react-native-nitro-modules'
import CameraViewConfig from '../nitrogen/generated/shared/json/CameraViewConfig.json'
export const Camera = getHostComponent<CameraProps, CameraMethods>(
  'Camera',
  () => CameraViewConfig
)
6. Rendering
And finally, render it:
function App() {
  return <Camera enableFlash={true} />
}
Props
Since every HybridView is also a HybridObject, you can use any type that Nitro supports as a property - including custom types (interface), ArrayBuffer, and even other HybridObjects!
For example, a custom <ImageView> component can be used to render custom Image types:
export interface Image extends HybridObject {
  readonly width: number
  readonly height: number
  save(): Promise<string>
}
import { type Image } from './Image.nitro.ts'
export interface ImageProps extends HybridViewProps {
  image: Image
}
export type ImageView = HybridView<ImageProps>
Then;
function App() {
  const image = await loadImage('https://...')
  return <ImageView image={image} />
}
Threading
Since Nitro bridges props directly to JS, you are responsible for ensuring thread-safety.
- If props are set normally via React, they will be set on the UI Thread.
 - If the user sets props on the view 
hybridRef(e.g. also if theHybridViewis passed to aHybridObjectin native), props could be set on a different Thread, like the JS Thread. 
Before/After update
To batch prop changes, you can override beforeUpdate() and afterUpdate() in your views:
- Swift
 - Kotlin
 
class HybridCameraView : HybridCameraViewSpec {
  // View
  var view: UIView = UIView()
  func beforeUpdate() { }
  func afterUpdate() { }
}
class HybridCameraView : HybridCameraViewSpec() {
  // View
  override val view: View = View(NitroModules.applicationContext)
  override fun beforeUpdate() { }
  override fun afterUpdate() { }
}
Callbacks have to be wrapped
Whereas Nitro allows passing JS functions to native code directly, React Native core doesn't allow that. Instead, functions are wrapped in an event listener registry, and a simple boolean is passed to the native side. Unfortunately React Native's renderer does not allow changing this behaviour, so functions cannot be passed directly to Nitro Views. As a workaround, Nitro requires you to wrap each function in an object, which bypasses React Native's conversion.
So every function (() => void) has to be wrapped in an object with one key - f - which holds the function: { f: () => void }
export interface CameraProps extends HybridViewProps {
  onCaptured: (image: Image) => void
}
export type CameraView = HybridView<CameraProps>
function App() {
  return <Camera onCaptured={(i) => console.log(i)} />
  return <Camera onCaptured={{ f: (i) => console.log(i) }} />
}
We are working on a fix here: facebook/react #32119
Methods
Since every HybridView is also a HybridObject, methods can be directly called on the object.
Assuming our <Camera> component has a takePhoto() function like so:
export interface CameraProps extends HybridViewProps { ... }
export interface CameraMethods extends HybridViewMethods {
  takePhoto(): Promise<Image>
}
export type CameraView = HybridView<CameraProps, CameraMethods>
To call the function, you would need to get a reference to the HybridObject first using hybridRef:
function App() {
  return (
    <Camera
      hybridRef={{
        f: (ref) => {
          const image = ref.takePhoto()
        }
      }}
    />
  )
}
Note: If you're wondering about the
{ f: ... }syntax, see "Callbacks have to be wrapped".
The ref from within hybridRef's callback is pointing to the HybridObject directly - you can also pass this around freely.