Reputation: 968
How would I load a .md markdown file into a react component? I have tried so many npm libraries through google searches and I cant find a good solution.
I want to load the .md file something like:
render() {
<div>
<MarkDown src="about.md" />
</div>
}
Upvotes: 88
Views: 108156
Reputation: 1741
1. Install react-markdown
npm i react-markdown
2. Use it in my react functional component
import { useEffect, useState } from 'react'
import ReactMarkdown from 'react-markdown'
import bookPath from './../Markdown/book.md'
const MyComponent = () => {
const [text, setText] = useState('')
useEffect(() => {
fetch(bookPath)
.then((response) => response.text())
.then((md) => {
setText(md)
})
}, [])
return (
<div>
<ReactMarkdown children={text} />
</div>
)
}
export default MyComponent
Upvotes: 3
Reputation: 432
The Updated Version of https://stackoverflow.com/a/42928796/12271495
import React, { Component } from "react";
import { marked } from "marked";
class App extends Component {
state = {markdown: ""}
componentDidMount() {
const readmePath = require("./blogs/README.md");
fetch(readmePath)
.then((response) => {
return response.text();
})
.then((text) => {
this.setState({
markdown: marked.parse(text),
});
});
}
render() {
const { markdown } = this.state;
return (
<section>
<article dangerouslySetInnerHTML={{ __html: markdown }}></article>
</section>
);
}
}
export default App;
Upvotes: 1
Reputation: 12783
Where I struggled with all these answers is the loading of the markdown file into frontend React. They make some kind of assumption as to what you're using.
For the world of React I would recommend MDX.
I'm using it with Next.js, but MDX is compatible with lots of Javascript bundlers and React libraries and does a lot of work to handle the bit that lots of people ignore here which is the loading in of non-JS syntax.
Note: MDX sticks more to the original style of Markdown without tables or the funky extras. If you want extra stuff you need to install MDX plugins
See these references:
Install with:
npm install @next/mdx @mdx-js/loader
next.config.js
:
For Next.js integration we don't have to touch the webpack config just wrap our existing next.config.js
with a withMDX
function:
/**
* @link https://github.com/vercel/next.js/tree/canary/packages/next-mdx#usage
*/
const withMDX = require('@next/mdx')()
// your existing next.config.js
const nextConfig = {
...
}
module.exports = withMDX(nextConfig)
about.mdx
:
Read more about this in Using MDX docs
# Just some regular markdown
any old rubbish **will do**
You can stick extra <Funky /> stuff in here but *regular markdown* works fine
AboutPage.jsx
:
Note below how now you don't need to do any fetch
ing or useEffect
, it's simply like loading in a JSON or JS file.
import React from "react";
import AboutMarkdown from "./about.mdx";
const AboutPage () => (
<AboutMarkdown />
);
Upvotes: 5
Reputation: 1575
Another option is to put the Markdown in a .js file, using the backtick ` character to enclose the Markdown as an untagged template literal. Like this:
const MD = `
**TERMS OF SERVICE**
Last Modified: 30 November 2021...`
export default MD
Then you can import
it like any other module.
Upvotes: 0
Reputation: 73
I wanted it to work using dynamic imports using react-markdown. My general code is below, you'll have to add a useEffect to call the function and put a reference to the state variable in the function return:
const [displayElement, setDisplayElement] = useState(null);
//Get markdown file
const fetchMarkdown = async (location) => {
console.log("MD location: ", location);
try {
//I figured out readmePath.default using print statements, left there in case
//someone wants them
const readmePath = await require("" + location);
//console.log(readmePath);
const response = await fetch(readmePath.default);
//console.log("response => ", response);
const text = await response.text();
//console.log(text);
// the state variable I am setting the markdown into, in my render function
// I have {displayElement}.
setDisplayElement(
<div className={styles.markdownContainer}>
<ReactMarkdown children={text} />
</div>
);
} catch (e) {
console.log("Markdown file: couldn't read =>", location, e);
}
};
The addition of the empty string in const readmePath = await require("" + location);
is required (hehe). I got that from here. I don't know why it works.
Upvotes: 0
Reputation: 19074
markdown-to-jsx provides very efficeint functionality to interact with markdown in React component.
It allows replacing/overriding of any HTML element with your Custom Component for markdown, here is the doc.
import React, { Component } from 'react'
import Markdown from 'markdown-to-jsx';
import README from './README.md'
class PageComponent extends Component {
constructor(props) {
super(props)
this.state = { md: "" }
}
componentWillMount() {
fetch(README)
.then((res) => res.text())
.then((md) => {
this.setState({ md })
})
}
render() {
let { md } = this.state
return (
<div className="post">
<Markdown children={md}/>
</div>
)
}
}
export default PageComponent
const PageComponent = ()=> {
let [ content, setContent] = useState({md: ""});
useEffect(()=> {
fetch(README)
.then((res) => res.text())
.then((md) => {
setContent({ md })
})
}, [])
return (
<div className="post">
<Markdown children={content.md}/>
</div>
)
}
Upvotes: 16
Reputation: 542
Similar to @Xing-Han-Lu's answer but with react Markdown. The concept uses useEffect
to load up the file then adds it to state using the useState
hook where it's accessible to reactMarkdown
import React, { useState, useEffect } from "react";
import ReactMarkdown from "react-markdown";
import file from "./md/posts.md";
export default function () {
const [markdown, setMarkdown] = useState("");
useEffect(() => {
fetch(file)
.then((res) => res.text())
.then((text) => setMarkdown(text));
}, []);
return (
<>
<ReactMarkdown source={markdown} />
</>
);
}
Upvotes: 18
Reputation: 6732
Install raw-loader
npm install raw-loader --save-dev
Update webpack.config.js
module.exports = {
//...
module: {
rules: [
// ...
{
test: /\.md$/,
use: "raw-loader",
},
],
},
};
Create markdown file (say App.md
)
# React & Markdown App
- Benefits of using React... but...
- Write layout in Markdown!
Import App.md
and use it in React component.
import React from "react";
import ReactMarkdown from 'react-markdown';
import AppMarkdown from './App.md';
function App() {
return (
<div>
<ReactMarkdown children={`${AppMarkdown}`} />
</div>
);
}
export default App;
Upvotes: 9
Reputation: 1016
I slightly modified this solution to use hooks and useEffect
(which is different from componentWillUpdate
but still works). If you built your app with create-react-app and you have a markdown document called document.md
, you can build your app in the following way:
import { useState, useEffect } from 'react';
import Markdown from 'markdown-to-jsx';
import mdDocument from './document.md';
const App = () => {
const [content, setContent] = useState("");
useEffect(() => {
fetch(mdDocument)
.then(res => res.text())
.then(md => { setContent(md) })
})
return (
<div><Markdown children={content} /></div>
)
}
export default App;
Upvotes: 1
Reputation: 1798
For Typescript + react please follow below steps:
declare module "*.md";
tsconfig.json -> CompilerOptions -> typeRoots
as following{
"compilerOptions": {
...
"typeRoots": [ "<types-directory-created-in-#1>", "./node_modules/@types"],
...
}
}
yarn add showdown
or npm install showdown
yarn add html-react-parser
or npm install html-react-parser
import React, { useEffect, useState } from 'react';
import showdown from 'showdown';
import parse from 'html-react-parser';
import readme from 'path/filename.md';
export default function ComponentName() {
const [html, setHTML] = useState("");
//Use componentDidMount(): if class based component to load md file
useEffect(() => {
fetch(readme)
.then(data => data.text())
.then(text => {
const converter = new showdown.Converter();
setHTML(converter.makeHtml(text));
})
}, []);
return (
<div>{parse(html)}</div>
)
}
Upvotes: 1
Reputation: 583
I have tried the above suggestions and deduced that after running a command
> npm install markdown
import ReactMarkdown from 'markdown';
it finally worked for me
Upvotes: -1
Reputation: 979
You should use react-markdown instead of the accepted answer, this solution doesn't use dangerouslySetInnerHTML
.
App.js
import React, { Component } from 'react';
import AppMarkdown from './App.md';
import ReactMarkdown from 'react-markdown';
class App extends Component {
constructor() {
super();
this.state = { markdown: '' };
}
componentWillMount() {
// Get the contents from the Markdown file and put them in the React state, so we can reference it in render() below.
fetch(AppMarkdown).then(res => res.text()).then(text => this.setState({ markdown: text }));
}
render() {
const { markdown } = this.state;
return <ReactMarkdown source={markdown} />;
}
}
export default App;
App.md
# React & Markdown App
* Benefits of using React... but...
* Write layout in Markdown!
Upvotes: 36
Reputation: 128771
I first import it like this:
import marked from "marked";
I then fetch my *.md file within React's componentDidMount
event and store it in my component's state using marked(text)
(where text
is the response):
componentDidMount() {
const readmePath = require("./Readme.md");
fetch(readmePath)
.then(response => {
return response.text()
})
.then(text => {
this.setState({
markdown: marked(text)
})
})
}
...and finally I render it on the page using the dangerouslySetInnerHTML
attribute:
render() {
const { markdown } = this.state;
return (
<section>
<article dangerouslySetInnerHTML={{__html: markdown}}></article>
</section>
)
}
Upvotes: 68
Reputation: 41
If you use Webpack (i.e. Electron React Boilerplate) then you can save a few steps by using Webpack loaders.
npm i -D html-loader markdown-loader marked
In webpack.config.renderer.dev.js:
import marked from 'marked';
const markdownRenderer = new marked.Renderer();
....
// Markdown
{
test: /\.md$/,
use: [
{
loader: 'html-loader'
},
{
loader: 'markdown-loader',
options: {
pedantic: true,
renderer: markdownRenderer
}
}
]
}
Then, in the React component it's simply a require and setting the HTML.
import knownIssues from '../assets/md/known-issues.md';
....
<p dangerouslySetInnerHTML={{ __html: knownIssues }} />
Lastly, Flow will report an error (it still works) when importing the markdown file. Add this to .flowconfig to make Flow treat md files as string assets (care of Webpack):
module.name_mapper.extension='md' -> '<PROJECT_ROOT>/internals/flow/WebpackAsset.js.flow'
Upvotes: 0
Reputation: 23929
A full working example with react-markdown
:
import React, { Component } from 'react'
import ReactMarkdown from 'react-markdown'
import termsFrPath from './Terms.fr.md'
class Terms extends Component {
constructor(props) {
super(props)
this.state = { terms: null }
}
componentWillMount() {
fetch(termsFrPath).then((response) => response.text()).then((text) => {
this.setState({ terms: text })
})
}
render() {
return (
<div className="content">
<ReactMarkdown source={this.state.terms} />
</div>
)
}
}
export default Terms
Upvotes: 77