79

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.

Code image

I want to load the .md file something like:

render() {
    <div>
        <MarkDown src="about.md" />
    </div>
}
James Donnelly
  • 126,410
  • 34
  • 208
  • 218
PizzaHead
  • 878
  • 1
  • 6
  • 13

15 Answers15

72

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
Dorian
  • 22,759
  • 8
  • 120
  • 116
  • solid. Thanks for the detail. How do you deal with the redirect its taking you to? – lopezdp Apr 15 '20 at 21:28
  • 3
    I get `Cannot find module './Terms.fr.md' or its corresponding type declarations` error. Using `const termsFrPath = require './Terms.fr.md'` works. – Toivo Säwén Mar 16 '21 at 15:58
  • 5
    if you are using typescript juste create a declaration.d.ts file at the root of your project and add : `declare module '*.md'` before importing it – Mathias Bradiceanu Mar 20 '22 at 16:10
64

I use marked (GitHub).

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>
  )
}
James Donnelly
  • 126,410
  • 34
  • 208
  • 218
  • 2
    Tried import "marked" and Typescript couldnt find declaration file – PizzaHead Mar 21 '17 at 13:44
  • 2
    I'm not familiar with TypeScript, but you'll need to install the package first. `npm install marked --save` should do the trick. – James Donnelly Mar 21 '17 at 13:46
  • 1
    Ah, found out i needed to install a type definition to make Typescript happy. https://www.npmjs.com/package/@types/marked. Thank you my friend – PizzaHead Mar 21 '17 at 13:49
  • You also need to initiate state via construct(props) method. – Chatri Sae-Tung Sep 23 '17 at 03:10
  • 3
    doesn't work for me ./src/pages/about.md Module parse failed: /Users/me/myproject/src/pages/about.md Unexpected character '#' (1:0) You may need an appropriate loader to handle this file type. SyntaxError: Unexpected character '#' (1:0) – decades Dec 11 '17 at 09:11
  • I want to load all markdown files inside folder (preview only). Any tip? – Ukasha Jun 07 '18 at 01:02
  • 2
    Check out https://stackoverflow.com/a/53975297/7784399 instead of this. It doesn't use the `dangerouslySetInnerHTML` attribute. – Kevin Jan 22 '19 at 16:42
34

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!
Kevin
  • 929
  • 1
  • 8
  • 13
16

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

Edit 2nd Aug'21

Functional Component
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>
    )
}
Nishchit
  • 18,284
  • 12
  • 54
  • 81
13

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} />
    </>
  );
}
Riunge Maina
  • 432
  • 6
  • 10
9

Approach using webpack loader

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;
Vlad
  • 6,402
  • 1
  • 60
  • 74
5

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 fetching 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 />
);
icc97
  • 11,395
  • 8
  • 76
  • 90
  • 1
    Just a note: the latest version of MDX loader doesn't work on react apps created using Create React App (CRA), as noted here https://github.com/mdx-js/mdx/discussions/1870. One suggestion to fix it involves downgrading `@mdx/loader` and `react-scripts` dependencies, which can be undesirable in context of security concerns. – Moistbobo Mar 13 '22 at 19:01
1

For Typescript + react please follow below steps:

  1. Create one type definition (index.d.ts) file in one of the project directory and put the following code.
declare module "*.md";
  1. Add tsconfig.json -> CompilerOptions -> typeRoots as following
{
     "compilerOptions": {
         ...
         "typeRoots": [ "<types-directory-created-in-#1>", "./node_modules/@types"],
         ...
     }
}
  1. Install two libraries showdown and html-react-parser

yarn add showdown or npm install showdown

yarn add html-react-parser or npm install html-react-parser

  1. In your component
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>
    )
}
Pramod Mali
  • 1,588
  • 1
  • 17
  • 29
1

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;

xhluca
  • 868
  • 5
  • 22
1

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;
Jan
  • 302
  • 3
  • 5
0

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'
0

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

jizhihaoSAMA
  • 12,336
  • 9
  • 27
  • 49
Otabek Butcher
  • 545
  • 8
  • 19
0

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.

Thomas Sloan
  • 63
  • 1
  • 2
  • 9
0

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.

ed94133
  • 1,477
  • 2
  • 19
  • 40
0

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
Green
  • 507
  • 10
  • 20