davinci
davinci

Reputation: 61

New line with react-markdown

I am trying to pull the markdown string from firebase database then using react-markdown to render it into Component. But the markdown string doesn't show correctly. I think the problem is due to start with new line.

I tried to declare the variable I put markdown in there. it works. markdown string which is created in Component

But the markdown string which is pulled from firebase database. it doesn't show correctly. markdown string which is pulled from firebase database

Here is my code

export default function BlogTemplate() {
  const { id } = useParams();
  useFirestoreConnect([
    {
      collection: "communications",
      doc: id
    }
  ]);
  const post = useSelector(
    state =>
      state.firestore.data.communications &&
      state.firestore.data.communications[id]
  );

  if (post) {
    const input =
      "# This is a header\n\nAnd this is a paragraph \n\n # This is header again"; // this is a markdown string created in Component
    const postContent = post.content; // This is the markdown string pulled from firebase database
    return (
      <Layout>
        <Container>
          <Content className="pt-5 pb-5">
            <ReactMarkdown source={input} />
            <ReactMarkdown source={postContent} />
          </Content>
        </Container>
      </Layout>
    );
  } else {
    return <p>Loading...</p>;
  }
}

Upvotes: 6

Views: 24365

Answers (8)

Mahesh Jamdade
Mahesh Jamdade

Reputation: 20249

Adding the following CSS to <p> tag worked for me.

Add this to your CSS

.line-break {
  white-space: pre-wrap;
  line-height: 1.5;
}

Apply the css to <p> tag only

<ReactMarkdown
    className={`${customClass} mb-2`}
    components={{
        // add a css for p tags
        p({ children }) {
            return <p className='line-break'>{children}</p>;
        },
        code(props) {
          ....
        }
    }}
    remarkPlugins={[remarkGfm]}
    children={value}
/>
</div>
);

output

You can test it at https://pastelog.vercel.app

enter image description here

Upvotes: 1

CodingYourLife
CodingYourLife

Reputation: 8596

As @George pointed out use this quickfix to demonstrate that adding 2 whitespaces would fix the issue:

text = text?.replace(/\n/gi, "  \n") ?? "";

As @Carneiro pointed out in the similar question and answer you can use the remark-breaks plugin.

Import like this:

import remarkBreaks from 'remark-breaks';

Use like this:

<ReactMarkdown remarkPlugins={[remarkBreaks]}>
    {"line1\nline2"}
</ReactMarkdown>

I checked the plugin code and it basically just wraps mdast-util-newline-to-break with 112K weekly downloads.

Upvotes: 0

CbeDroid1614
CbeDroid1614

Reputation: 111

Solved: One way is to simply replace the new lines with HTML <br/> should solve the problem.

Use ReactMarkdown with rehype-raw plugin.


text.replace(/\\n/gi, "\n").replace(/\n/gi, "<br/>");

e.g:

import rehypeRaw from "rehype-raw";
import remarkGfm from "remark-gfm";


const SomeComponent = () => {

 const text = "Foo\nBar\nBaz";

 return (
  <ReactMarkdown
     children={text}
     remarkPlugins={[remarkGfm]}
     rehypePlugins={[rehypeRaw]}
   />
 )
}
// Output 

Foo
<br/>
Bar
<br/>
Baz

Upvotes: 1

Francois MUGOROZI
Francois MUGOROZI

Reputation: 299

Firestore stores the string as a plain test, some extra space, a new line and other characters are somehow removed by firestore.

I fixed this by saving my content encoded as base64 string

 const encoded = Buffer.from(content).toString("base64") // on node nevironment

and then decode it back when I want to use it

 const decoded = Buffer.from(encoded,"base64").toString("utf8") // on node environment

If you want to do encoding on browser, you will need to use other technics like "TextEncoder"

Upvotes: 0

Heewon Gwak
Heewon Gwak

Reputation: 31

If pre-wrap is applied to the entire markdown, the display will be weird in other parts, so I solved it like this.

import { styled } from '@mui/material/styles';
import { FC, ReactNode } from 'react';
import ReactMarkdown from 'react-markdown';

type Props = {
  className?: string;
  children?: ReactNode;
  markdown: string;
};

const SC = {
  P: styled('p')(({ theme }) => ({
    whiteSpace: 'pre-wrap',
  })),
};

const MarkDownView: FC<Props> = React.memo(function MarkdownView({ className, markdown }) {
  return (
    <ReactMarkdown
      className={className}
      remarkPlugins={[remarkGfm]}
      components={{
        p({ className, children, ...props }) {
          return <SC.P className={className}>{children}</SC.P>;
        },
      }}
    >
      {markdown}
    </ReactMarkdown>
  );
});

export default MarkDownView;

Target is Paragraphs tag

Upvotes: 3

amirreza mojarad
amirreza mojarad

Reputation: 331

first create css class forexample

.foo {white-space: 'pre-wrap';}

then add classname to markdown component

<ReactMarkdown source={input} className="foo" />

Upvotes: 9

Patricio Toledo
Patricio Toledo

Reputation: 11

Hope help someone with this, when you retreive a string from firebase, I don't know why it adds an extra "/" to the new lines. Eg: "\n" => "\n". So you need to replace it like this:

const parseToMarkDown = (str: string): string => {
    return str.replace(/\\n/g, "\n");
}

Upvotes: 1

George
George

Reputation: 59

It seems that you need to add 2 empty spaces before \n like this: "This is a header \n \nAnd this is a paragraph".

Let me know if that works for you.

More info here: https://github.com/remarkjs/react-markdown/issues/273

Upvotes: 4

Related Questions