Spaces:
Sleeping
Sleeping
In this guide, you'll configure your plugin to use [React](https://react.dev/). It assumes that you already have a plugin with a [[Views|custom view]] that you want to convert to use React. | |
While you don't need to use a separate framework to build a plugin, there are a few reasons why you'd want to use React: | |
- You have existing experience of React and want to use a familiar technology. | |
- You have existing React components that you want to reuse in your plugin. | |
- Your plugin requires complex state management or other features that can be cumbersome to implement with regular [[HTML elements]]. | |
## Configure your plugin | |
1. Add React to your plugin dependencies: | |
```bash | |
npm install react react-dom | |
``` | |
2. Add type definitions for React: | |
```bash | |
npm install --save-dev @types/react @types/react-dom | |
``` | |
3. In `tsconfig.json`, enable JSX support on the `compilerOptions` object: | |
```ts | |
{ | |
"compilerOptions": { | |
"jsx": "preserve" | |
} | |
} | |
``` | |
## Create a React component | |
Create a new file called `ReactView.tsx` in the plugin root directory, with the following content: | |
```tsx title="ReactView.tsx" | |
export const ReactView = () => { | |
return <h4>Hello, React!</h4>; | |
}; | |
``` | |
## Mount the React component | |
To use the React component, it needs to be mounted on a [[HTML elements]]. The following example mounts the `ReactView` component on the `this.containerEl.children[1]` element: | |
```tsx | |
import { StrictMode } from "react"; | |
import { ItemView, WorkspaceLeaf } from "obsidian"; | |
import { Root, createRoot } from "react-dom/client"; | |
import { ReactView } from "./ReactView"; | |
const VIEW_TYPE_EXAMPLE = "example-view"; | |
class ExampleView extends ItemView { | |
root: Root | null = null; | |
constructor(leaf: WorkspaceLeaf) { | |
super(leaf); | |
} | |
getViewType() { | |
return VIEW_TYPE_EXAMPLE; | |
} | |
getDisplayText() { | |
return "Example view"; | |
} | |
async onOpen() { | |
this.root = createRoot(this.containerEl.children[1]); | |
this.root.render( | |
<StrictMode> | |
<ReactView />, | |
</StrictMode>, | |
); | |
} | |
async onClose() { | |
this.root?.unmount(); | |
} | |
} | |
``` | |
For more information on `createRoot` and `unmount()`, refer to the documentation on [ReactDOM](https://react.dev/reference/react-dom/client/createRoot#root-render). | |
You can mount your React component on any `HTMLElement`, for example [[Plugins/User interface/Status bar|status bar items]]. Just make sure to clean up properly by calling `this.root.unmount()` when you're done. | |
## Create an App context | |
If you want to access the [[Reference/TypeScript API/App/App|App]] object from one of your React components, you need to pass it as a dependency. As your plugin grows, even though you're only using the `App` object in a few places, you start passing it through the whole component tree. | |
Another alternative is to create a React context for the app to make it globally available to all components inside your React view. | |
1. Use `createContext()` to create a new app context. | |
```tsx title="context.ts" | |
import { createContext } from "react"; | |
import { App } from "obsidian"; | |
export const AppContext = createContext<App | undefined>(undefined); | |
``` | |
2. Wrap the `ReactView` with a context provider and pass the app as the value. | |
```tsx title="view.tsx" | |
this.root = createRoot(this.containerEl.children[1]); | |
this.root.render( | |
<AppContext.Provider value={this.app}> | |
<ReactView /> | |
</AppContext.Provider> | |
); | |
``` | |
3. Create a custom hook to make it easier to use the context in your components. | |
```tsx title="hooks.ts" | |
import { useContext } from "react"; | |
import { AppContext } from "./context"; | |
export const useApp = (): App | undefined => { | |
return useContext(AppContext); | |
}; | |
``` | |
4. Use the hook in any React component within `ReactView` to access the app. | |
```tsx title="ReactView.tsx" | |
import { useApp } from "./hooks"; | |
export const ReactView = () => { | |
const { vault } = useApp(); | |
return <h4>{vault.getName()}</h4>; | |
}; | |
``` | |
For more information, refer to the React documentation for [Passing Data Deeply with Context](https://react.dev/learn/passing-data-deeply-with-context) and [Reusing Logic with Custom Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks). | |