This is a plugin for Obsidian (https://obsidian.md).
It allows you to write and use React components with Jsx inside your Obsidian notes.
It is highly recommended that you also install the Editor Syntax Highlight Plugin when using this plugin.
There are two methods for creating react components.
Put a note anywhere in your vault and give it the property defines-react-components
. You can also optionally set a specific namespace (series of words separated by dots), using the react-components-namespace
property.
This can either be done in the frontmatter, like so:
---
defines-react-components: true
react-components-namespace: projects.test
---
or (if dataview is installed) using dataview inline properties
defines-react-components:: true
react-components-namespace:: projects.test
Then, in your note, to define a component, write a code block like this
```jsx:component:MyComponent
return <div>Hello {props.name}!</div>
```
This will create a component called MyComponent
, located in the projects.test
namespace (or in the global namespace if you left the property unspecified).
You can then use the component like this:
```jsx:
<MyComponent name={"World"}/>
```
Or, using inline syntax.
`jsx:<MyComponent name={"World"}/>`
If you are in a note which uses a separate namespace, you can access the component like so:
`jsx:<projects.test.MyComponent name={"World"}/>`
An alternative way of creating components is component notes. This approach treats an entire markdown file as the definition of a single react component. A benefit of this approach is that you can open the note in, for example, visual studio code, to get full syntax highlighting and some code autocompletion.
In order to use component notes, you must first specify a folder for the Jsx functions / react components.
Every note in this directory will be interpreted as the content of a Jsx function (implicitly of the form props=>{your code here}
)
Every file becomes a function/react component with the same name as the note.
The syntax for writing components is regular Jsx Syntax
The content of your component file is implicitly wrapped in props=>{...}
. This means that you don't write the function signature yourself. You do, however, need to include the return
keyword in your code.
Other things to keep in mind:
- Since the notes are interpreted as function variables, they must follow the javascript variable naming rules.
- Variable names cannot contain spaces.
- Variable names must begin with a letter, an underscore (_) or a dollar sign ($).
- Variable names can only contain letters, numbers, underscores, or dollar signs.
- Variable names are case-sensitive.
- Certain words may not be used as variable names, because they have other meanings within JavaScript. Check out this complete list of the reserved words.
- In order to be used as a React component, the first letter of the function must be capitalized.
Components can be used like this:
```jsx:
<MyComponent name={"World"}/>
```
Or, using inline syntax.
`jsx:<MyComponent name={"World"}/>`
If you are in a note which uses a separate namespace, you can access the component like so:
`jsx:<projects.test.MyComponent name={"World"}/>`
When using the codeblock syntax, (```jsx:
), the code can be multiple lines. The last statement is implicitly returned and rendered.
The react components have access to everything inside the global namespace.
Besides this, the components have access to React
, ReactDOM
, useState
, and useEffect
.
This allows you to easily write functional components.
Besides that, you can also access the file context through the hook call useContext(ReactComponentContext);
. This can then be used to, for example, access the frontmatter as follows:
```jsx:component:ComponentWithFrontmatter
const ctx = useContext(ReactComponentContext);
var frontmatter = ctx.markdownPostProcessorContext.frontmatter;
return <h1>{frontmatter.title}</h1>
```
Feel free to contribute.
You can create an issue to report a bug, suggest an improvement for this plugin, ask a question, etc.
You can make a pull request to contribute to this plugin development.
- jsx codeblocks broke in a recent obsidian update (See issue #26). With this update, they should work better again.
- Added support for embedded notes in the
Markdown
component (See issue #25).
- A lot of
console.log
calls were used when adding the live preview support. These have now been removed.
- The plugin should now work on in the app version of Obsidian again.
- Components should no longer dissappear when navigating between views.
- Removed flickering of components at startup.
- It is now possible to write codeblocks like
- Old components are now more reliably removed/disposed during rerenders.
- The plugin now has support for writing
jsx:
instead ofjsx-
.- This new syntax is compatible with the Editor Syntax Highlight Plugin:
- You can now also restrict how / from where you access components through the
react-components-namespace
property. See Readme for details. - The Readme has been updated.
- It is now possible to add frontmatter data to the component notes. (it will be ignored by the javascript parser).
- Notes with the frontmatter attribute
use-as-note-header
will be used as a header for all notes in the vault.
- Issue with loading components on obisian start has been resolved. (Issue #19)
- Issue with loading plugin on Obsidan Mobile has been resolved.
- Typescript syntax is now supported
- Unused dependencies removed
- Added support for url based imports, such as
import styled from 'https://cdn.skypack.com/styled-components/'
- Improved stability of component loading
- Updating the
src
prop of theMarkdown
component previously did not cause the component to rerender. This is now fixed. - For developers: you can now create a
.vault_plugin_dir
file containing the path to the plugin in your vault: (e.g.path\to\my\vault\.obsidian\plugins\obsidian-react-components
). Thenyarn build
will automatically copy the compiled files to the correct place. So you only have to reload the plugin in Obsidian to see changes take effect.
-
Add a new setting to disable component refreshing
- Useful if re-rendering of components is costly, such as if the component makes API calls.
-
Make component loading more reliable (Resolves issue #13)
-
Significantly improve error handling
- All errors are rendered as react components. You can click a button in the component to show the error in the console.
-
Add a command to manually refresh components
Obsidian React Components: Refresh React Components
-
Replace
isPreviewMode
withuseIsPreview
, which check the current pane of the component instead of the currently active component (Resolves issue #12)Example:
const isPreview = useIsPreview() if(isPreview) { // this only happens if the pane which the component is attached to is in preview mode. }
- Made some minor fixes based on feedback in the community-plugins PR
- Added a
Markdown
component, which can be used to render makdown.- Usage:
`jsx-<Markdown src={"* This is a bullet"}/>`
- Usage:
- Added
obsidian
to the component scope - Added more hooks:
useCallback
,useContext
,useMemo
,useReducer
,useRef
-
@lucasew Added an
isPreviewMode
function to the component scope. (PR #5)There is a rule that in React you must call the same hooks at every render so early returns are not good.
The user can easily check if the note is on preview mode inside its component and can return null if it's the case.
-
Components which contain sub-components are now correctly updated when the sub-component code is modified. (PR #11)
-
Users are now warned when creating components with invalid names (PR #10)
- Basic functionality of the plugin implemented
// file: Counter.md
const [count, setCount] = useState(0)
return (
<div>
<p>You clicked me {count} times!!!</p>
<button onClick={() => setCount(count + 1)}>
{props.source}
</button>
</div>
)
// file: Clock.md
const [date, setDate] = useState(new Date());
useEffect(() => {
var timerID = setInterval( () => setDate(new Date()), 1000 );
return function cleanup() {
clearInterval(timerID);
};
});
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {date.toLocaleTimeString()}.</h2>
</div>
);
// file: rand.md
return Math.random()
// file: DiceRoller.md
let diceRoll = ()=>Math.ceil(rand()*props.sides)
let [num, setNum] = useState(diceRoll())
return (<span>
<button onClick={()=>setNum(diceRoll())}> Roll the {props.sides}-sided Die</button>
<span>The number is {num}</span>
</span>)
// file: Testcomponent.md
return (
<div style={{color: "blue"}}>
<Clock/>
<Counter source={props.source}/>
</div>
)
Obsidian React Components is licensed under the GNU AGPLv3 license. Refer to LICENSE for more information.