Spaces:
Sleeping
In this guide, you'll configure your plugin to use React. 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
Add React to your plugin dependencies:
npm install react react-dom
Add type definitions for React:
npm install --save-dev @types/react @types/react-dom
In
tsconfig.json
, enable JSX support on thecompilerOptions
object:{ "compilerOptions": { "jsx": "preserve" } }
Create a React component
Create a new file called ReactView.tsx
in the plugin root directory, with the following content:
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:
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.
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.
Use
createContext()
to create a new app context.import { createContext } from "react"; import { App } from "obsidian"; export const AppContext = createContext<App | undefined>(undefined);
Wrap the
ReactView
with a context provider and pass the app as the value.this.root = createRoot(this.containerEl.children[1]); this.root.render( <AppContext.Provider value={this.app}> <ReactView /> </AppContext.Provider> );
Create a custom hook to make it easier to use the context in your components.
import { useContext } from "react"; import { AppContext } from "./context"; export const useApp = (): App | undefined => { return useContext(AppContext); };
Use the hook in any React component within
ReactView
to access the app.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 and Reusing Logic with Custom Hooks.