React display line breaks from saved textarea
Asked Answered
M

7

145

Using Facebook React. In a settings page, I have a multiline textarea where a user can enter multiline text (in my case, an address).

<textarea value={address} />

When I try to display the address, so something like {address}, it doesn't show the line breaks and is all on one line.

<p>{address}</p>

Any ideas how to solve this?

Marquita answered 28/3, 2016 at 10:2 Comment(1)
I spent a lot of time debugging this so I'm leaving this for future selves like me: check your react states if you think that saving into the state is removing the newline characters.Demavend
C
400

There's no reason to use JS. You can easily tell the browser how to handle newline using the white-space CSS property:

white-space: pre-line;

pre-line

Sequences of whitespace are collapsed. Lines are broken at newline characters, at <br>, and as necessary to fill line boxes.

Check out this demo:

<style>
  #p_wrap {
    white-space: pre-line;
  }
</style>

<textarea id="textarea"></textarea>
<p id="p_standard"></p>
<hr>
<p id="p_wrap"></p>
<script>
  textarea.addEventListener('keypress', function(e) {
    p_standard.textContent = e.target.value
    p_wrap.textContent = e.target.value
  })
</script>

browser support

Clydeclydebank answered 7/6, 2017 at 13:15 Comment(5)
This works multiple ways: if you try to render a line break (<br />) in JSX through a variable, it will "safely" render the markup instead of adding the line break. Using escape characters with this CSS works around that safety mechanism as well as fixing the stated issue.Raspberry
very nice and simple solution. Now all new line characters are working as expected for texarea.Desrochers
This is one of those answers I feel like upvoting multiple times. Thanks a bunch @enanupeBucher
This is the one.Kristinkristina
I don't know what's going on here but this doesn't work for me in Chrome with React 18, it just renders the \n.Maladroit
M
47

This is to be expected, you would need to convert the new line (\n) characters to HTML line breaks

An article about using it in react: React Newline to break (nl2br)

To quote article:

Because you know that everything in React is functions, you can't really do this

this.state.text.replace(/(?:\r\n|\r|\n)/g, '<br />')

Since that would return a string with DOM nodes inside, that is not allowed either, because has to be only a string.

You then can try do something like this:

{this.props.section.text.split(“\n”).map(function(item) {
  return (
    {item}
    <br/>
  )
})}    

That is not allowed either because again React is pure functions and two functions can be next to each other.

tldr. Solution

{this.props.section.text.split(“\n”).map(function(item) {
  return (
    <span>
      {item}
      <br/>
    </span>
  )
})}

Now we're wrapping each line-break in a span, and that works fine because span’s has display inline. Now we got a working nl2br line-break solution

Messieurs answered 28/3, 2016 at 10:9 Comment(7)
Thanks Mark, works like a charm. Any chance you could put the answer here? Stackoverflow doesn't like external links (could disappear, harder to view list of answers,...) and I'll mark your answer as correct.Marquita
Note to others: You also need to add a key attribute to the span because it's in a loop. so it would be ....map(function (item, i) { return <span key={i}>{item}<br/></span> })Marquita
Great answer! This was helpful for me. ALSO, if you have MULTIPLE line breaks, you can prevent the excess <br /> from rendering for the last item: ....map(function (item, i, arr) { return <span key={i}>{item}{ arr.length-1 === i ? null : <br/>}</span> }). Notice that I included the 3rd argument for .map()Jenicejeniece
I don't understand how you got this to work. What I get displayed in the <textarea> is [object Object]Vino
@Vino It's to display HTML text not in a textarea. Does that help?Marquita
related- if you want to leave single line-breaks untouched, but treat 2+ line breaks as paragraph breaks: return text.trim().split(/\n{2,}/).map(p => <p>{p}</p>)Xylotomous
Thank you very much. Wonderfully explained..Wellgroomed
H
21

The solution is to set the property white-space on the element displaying the content of your textarea:

white-space: pre-line;
Haroldharolda answered 1/11, 2017 at 11:52 Comment(0)
H
4

Pete's previous proposal with standalone component is great solution although it misses one important thing. Lists needs keys. I adjusted it a bit and my version (without console warnings) looks like this:

const NewLineToBr = ({ children = '' }) => children.split('\n')
  .reduce((arr, line, index) => arr.concat(
    <Fragment key={index}>
      {line}
      <br />
    </Fragment>,
  ), [])

It uses React 16's Fragments

Hephzipa answered 6/8, 2018 at 12:51 Comment(1)
index as keys are only safe if you are sure the mapped data is "static" and won't ever change in its lifecycle, otherwise you are likely to have issues with it.Clydeclydebank
V
3

A small addition to answers above: white-space property should better be used with word-wrap to prevent overflowing.

p {
  white-space: pre-wrap;
  word-wrap: break-word;   
}
Valse answered 30/9, 2021 at 17:7 Comment(4)
I think you mean word-break? word-wrap is not in the CSS specUrsulaursulette
w3schools.com/cssref/css3_pr_word-wrap.phpValse
This is exactly why we don't like w3schools. That information is out of date. word-wrap is a non-standard obsolete implementation and should no longer be used. For legacy reasons, UAs must treat word-wrap as a legacy name alias of the overflow-wrap property.Ursulaursulette
I'm not attacking anyone. This answer is incorrect, right now. I don't really care when the question is from. Anyone seeing this answer now, should know that this syntax is legacy and shouldn't be used. BTW this was true 2 years ago as well as now.Ursulaursulette
O
2

As of React 16 a component can return an array of elements, which means you can create a component like this:

export default function NewLineToBr({children = ""}){
  return children.split('\n').reduce(function (arr,line) {
    return arr.concat(
      line,
      <br />
    );
  },[]);
}

which you'd use like this:

<p>
  <NewLineToBr>{address}</NewLineToBr>
</p>
Oh answered 18/4, 2018 at 5:24 Comment(1)
I had to change '\n' to '\\n'.Marolda
D
0

Love webit version. I did not know about the Fragment component, it is so useful. No need to use the reduce method though. Map is enough. Also, list do need keys in react , but it is bad habit to use index from the iterating method for it. eslint kept on smashing this in my warning until I had the confusion bug. So it'd look like this :

const NewLine = ({ children }) =>
   children.split("\n").map(line => (
    <Fragment key={uuidv4()}>
      {line}
      <br />
    </Fragment>
  ));
Demoralize answered 7/8, 2018 at 14:55 Comment(1)
It's a bad idea using uuids as key in React because EACH new render will consider this element is new and thus adapt the DOM.Clydeclydebank

© 2022 - 2024 — McMap. All rights reserved.