/obsidian-react-components

Write and use React (Jsx) components in your Obsidian notes.

Primary LanguageTypeScriptGNU Affero General Public License v3.0AGPL-3.0

GitHub release (latest SemVer) GitHub All Releases

Obsidian React Components

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.

Demonstration

React Components Demo

Getting Started

There are two methods for creating react components.

Using Code Blocks

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"}/>`

Using Component Notes

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.

image

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.

Writing Components

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.

Using Components

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.

Component Scope

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>
```

Contributing

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.

Changelog

0.1.6 (2022-01-22) Obsidian v0.13.19 support

  • 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).

0.1.5 (2022-01-03) Removed Debug Logging

  • A lot of console.log calls were used when adding the live preview support. These have now been removed.

0.1.4 (2022-01-02) App Fix

  • The plugin should now work on in the app version of Obsidian again.

0.1.3 (2022-01-01) Basic Live Preview Support

  • Both code blocks and inline code is supported. live preview

0.1.2 (2021-12-03) Component Codeblocks + improved startup and stability

  • Components should no longer dissappear when navigating between views.
  • Removed flickering of components at startup.
  • It is now possible to write codeblocks like
    • ```jsx::ComponentName
      someText
      ```

      Which is equivalent to

      ```jsx:
      <ComponentName src={`someText`}/>
      ```

      but less cluttered.

      Example: Component Codeblock Example

0.1.1 (2021-08-29) Improved component unmounting

  • Old components are now more reliably removed/disposed during rerenders.

0.1.0 (2021-08-27) Added Alternative code block Syntax and Namespaces

  • The plugin now has support for writing jsx: instead of jsx-.
  • 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.

0.0.9 (2021-08-26) Frontmatter Support and Header Components

  • 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.
    • This allows you to do things like this: header component demo
    • note: only use this frontmatter attribute on at most one vault component.

0.0.8 (2021-08-25) Minor rendering fix

  • Issue with loading components on obisian start has been resolved. (Issue #19)

0.0.7 (2021-07-20) Add support for mobile, Typescript

  • Issue with loading plugin on Obsidan Mobile has been resolved.
  • Typescript syntax is now supported
  • Unused dependencies removed

0.0.6 (2021-06-19) Add support for skypack imports, bugfixes

  • Added support for url based imports, such as import styled from 'https://cdn.skypack.com/styled-components/'
    • Example:
  • Improved stability of component loading

0.0.5 (2021-05-22) Enable dynamic updates of Markdown Rendering component, minor changes

  • Updating the src prop of the Markdown 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). Then yarn 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.

0.0.4 (2021-05-20) Improved Component Loading and Error Handling + useIsPreview

  • 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 with useIsPreview, 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.
    }

0.0.3 (2021-05-10) Markdown rendering component, more hooks, and minor fixes

  • 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"}/>`
  • Added obsidian to the component scope
  • Added more hooks: useCallback, useContext, useMemo, useReducer, useRef

0.0.2 (2021-05-10) New functionality, bug fixes, and refactoring

  • @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)

0.0.1 (2021-05-04) First Release

  • Basic functionality of the plugin implemented

Example components

// 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>
)

License

Obsidian React Components is licensed under the GNU AGPLv3 license. Refer to LICENSE for more information.