Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: RawHTML Component #129

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

RFC: RawHTML Component #129

wants to merge 1 commit into from

Conversation

drKnoxy
Copy link

@drKnoxy drKnoxy commented Nov 6, 2019

No description provided.


# Alternatives

- https://github.com/remarkablemark/html-react-parser
Copy link

@milesj milesj Nov 6, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or safely render HTML (dangerously is bad). https://github.com/milesj/interweave

@theKashey
Copy link

One day there was a proposal to use Fragment for this, ie <Fragment dangerouslySetInnerHTML...> as it does not include "any wrapping element"

@dangayle
Copy link

@theKashey facebook/react#12014


# Motivation

Today in React, one must wrap dangerous html in an element ```<div dangerouslySetInnerHtml={{__html: `<span>ok</span>`}} />```.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In order to avoid adding new component, maybe would be better to extend Fragment?

Example:

<Fragment dangerouslySetInnerHtml={{__html: '...'}} />

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See this comment on the issue linked directly above. It seems this RFC was created because the specific need of raw HTML is one created at the ReactDOM level, while Fragment comes from React proper.

@frank-dspeed
Copy link

I only need to point out while it is not relevant directly I have never seen an HTML Processing Framework that does not allow to easily use RAW HTML respect that point is earned by React. I will keep that example referenced for years it will illustrate how scary you can design a frontend lib.

At present people need to parse HTML into a DOM Tree to parse it back into react create an element and react itself offers nothing to assist in that process.

compare that to for example vue it uses raw html :) nothing needed to use rawHtml :)

@b0z1
Copy link

b0z1 commented Apr 17, 2020

I really dont understand why this is not possible right now...

@smetzdev
Copy link

I think, since React gets used more and more with things like gatsby and thus with CMS's, a RawHTML-Component would make sense.

@tyteen4a03
Copy link

tyteen4a03 commented Jun 19, 2020

Is there a workaround that is available now?

EDIT: Look like the most solid workaround is Interweave - it supports SSR as well!

@frank-dspeed
Copy link

@tyteen4a03 yes that is one possible solution the general solution is to use a parser like "parse5": "^5.1.1", thats what your package is using and write a tree adapter for react i am working on one at present

@halfzebra
Copy link

halfzebra commented Jun 25, 2020

I feel like this would be a very helpful feature for the project I'm currently working on. 👍

Doing universal rendering with micro frontends created a demand for being able to inject raw HTML inside React components for us and I don't feel like serving the entire HTML parser to the client just to get rid of the redundant wrapper.

Is there a consensus on RawHtml vs Fragments?

I hope this gets RFC some attention!

@ThewBear
Copy link

This feature will make injecting custom html into _document in next.js much easier. vercel/next.js#3105 vercel/next.js#3904

@jayphelps
Copy link

Hey @bvaughn 👋 you mentioned the best way forward is the RFC process. Can you help direct us what the next step is from here?

I saw the README mentioned "Eventually, the team will decide whether the RFC is a candidate for inclusion" but it's unclear if we need to ping you all, or maybe submit this RFC for team review perhaps?

@bvaughn
Copy link
Collaborator

bvaughn commented Sep 1, 2020

@jayphelps RFC process is still the appropriate path for this kind of issue/feature, but we can't guarantee any kind of timeline for RFC review or approval. (For what it's worth, the team does generally try to read and keep up-to-date with open RFCs. The approval process can be very time consuming on our side though and we just don't have the bandwidth for much extra at the moment.)

@tonymcneil
Copy link

My use case for this is in a gatsby project using server side rendering (gatsby-ssr.js) to populate the contents of head with the inline script for a NewRelic Browser Agent fetched using this api which produces a json document with the NewRelic loader script e.g.:

{
  "browser_applications": [
    {
      "id": 123456789,
      "name": "my-app-name",
      "browser_monitoring_key": "NRJS-9999999999999999999",
      "loader_script": "<script type=\"text/javascript\">\n;window.NREUM||(NREUM={});NREUM.init ... snip ...

So wrapping the contents of loader_script in a div would of-course be incorrect. There are other ways I can solve this, however the proposed RawHTML would be the cleanest!

```

```html
<html><head><script src="/app.js"></script> <link href="/styles.css" /></head><body>hello</body></html>

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this RFC, but I'm not sure if this case is clear enough 🤔 Just to make sure, do we really need this feature? No workaround here?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have the same problem and I just want to inject a JS snippet.

@gaearon
Copy link
Member

gaearon commented Aug 18, 2021

In the spirit of #182 — no particular update on this. We're doing work on a new streaming server renderer (reactwg/react-18#37) so I'll check with the team if there are any new constraints that would influence this.

@sassanh
Copy link

sassanh commented Oct 24, 2021

A use case of this is to render something like this:

['<p>', someReactElement, '</p>']

with react fragments accepting dangerouslySetInnerHTML it would be written as:

[
  <React.Fragment dangerouslySetInnerHTML={{__html: '<p>'}} />,
  someReactElement,
  <React.Fragment dangerouslySetInnerHTML={{__html: '</p>'}} />
]

As far as I understand if this array is generated dynamically, this is something not possible to achieve with React and there is no workaround.

Edit: Does anyone know if it is possible at all to support this with current features of the DOM?

@frank-dspeed
Copy link

frank-dspeed commented Oct 25, 2021

@sassanh it is possible the current state is you take a HTML/Parser and then you write a small script that translates the resulting PARSER-DOM to React-DOM in this issue are some links to existing solutions you should read it complet

https://github.com/milesj/interweave/

@sassanh
Copy link

sassanh commented Oct 25, 2021

@frank-dspeed Thanks! But I took a quick look but I really doubt if someReactElement between <p> and </p> will be rendered in a way that it preserves its event listeners or any other logic attached to it, I think interweave will just render the HTML (which I'm already able to do) but what I'm looking for is to preserve the react elements logic (they may have onClick, etc attached to their elements, timers set with setTimeout, fetching data from servers and render stuff based on that, etc)
Please let me know if interweave can handle it as well.

@frank-dspeed
Copy link

frank-dspeed commented Oct 25, 2021

@sassanh as interwave is a react-element you can put your other react element next to it

<inter.... /><your-component ..... /><inter ...... />

[
  <inter ..../>,
  someReactElement,
  <inter ... />
]

with the matchers you could code a custom matcher that injects your react element into a single <inter ... />

@sassanh
Copy link

sassanh commented Oct 25, 2021

I think interweave is not solving the problem I mentioned, if I wanted to parse the text containing the HTML elements and get aware of its structure I could resolve the issue without interweave too, but if React supported something like this:

[
  <ReactDOM.RawHTML dangerouslySetInnerHTML={{__html: '<p>'}} />,
  someReactElement,
  <ReactDOM.RawHTML dangerouslySetInnerHTML={{__html: '</p>'}} />
]

which I think is extremely hard to support, then I didn't need to be aware of the structure of the string passing to dangerouslySetInnerHTML and it would work with things like this out of the box:

[
  <ReactDOM.RawHTML dangerouslySetInnerHTML={{__html: veryComplexHTMLPart1}} />,
  aReactElementBasedOn_veryComplexHTMLPart2_,
  <ReactDOM.RawHTML dangerouslySetInnerHTML={{__html: veryComplexHTMLPart3}} />
]

@EECOLOR
Copy link

EECOLOR commented Nov 14, 2021

A usecase that would be a bit easier to achieve:

    <>
      {/* It is not possible to render the html of a React-rendered component without a container
          because dangerouslySetInnerHTML is the only route to get raw html into the resulting html */}
      <kaliber-component-container dangerouslySetInnerHTML={{ __html: renderedComponent }} />

      {/* Use render blocking script to remove the container and supply the correct  comment nodes.
          This ensures the page is never rendered with the intermediate structure */}
      <script dangerouslySetInnerHTML={{ __html: restructureDomNodes(componentInfo) }} />
    </>
function restructureDomNodes(componentInfo) {
  return `|var d=document,s=d.currentScript,p=s.parentNode,c=s.previousSibling;
          |p.setAttribute('${containerMarker}','');                             // set marker on container so we can retrieve nodes that contain components
          |p.replaceChild(d.createComment('start'),c);                          // replace kaliber-component-container element with a 'start' comment
          |p.insertBefore(d.createComment(JSON.stringify(${componentInfo})),s); // create a comment containing the component info
          |Array.from(c.childNodes).forEach(x=>{p.insertBefore(x,s)});          // insert all children from the kaliber-component-container element
          |p.replaceChild(d.createComment('end'),s);                            // create an 'end' comment
          |`.replace(/^\s*\|/gm, '').replace(/\s*\/\/.*$/gm, '').replace(/\n/g, '')
}

We use this to render components without a container on the server. On the client we extract the nodes to hydrate them.

This strategy allows us to render layout containers on the server and have CSS layout their interactive children without the hassle of an 'unneeded' container around the interactive child.

@satya164
Copy link
Member

@sassanh <React.Fragment dangerouslySetInnerHTML={{__html: '<p>'}} />

React is a cross-platform framework (React Native, Ink, other custom renderers etc.), so DOM-specific props on React.Fragment is unlikely.

@sassanh
Copy link

sassanh commented Nov 15, 2021

@satya164 Right, I will replace React.Fragment with ReactDOM.RawHTML then.

@benmcginnis
Copy link

This probably isn't a huge interest, but I'm working on writing a tool to translate google closure templates (aka soy templates) into React/JSX and this would be really helpful in maintaining the output behavior

@zomars
Copy link

zomars commented May 29, 2022

I know this is a hack but for my use case this allows me to inject arbitrary html inside head tags:

const RawHtml = ({ html = "" }) => (
  <script dangerouslySetInnerHTML={{ __html: `</script>${html}<script>` }} />
);

Usage:

const EmailHead = ({ title = "" }) => {
  return (
    <head>
      <title>{title}</title>
      <RawHtml html={`<!--[if !mso]><!--><meta http-equiv="X-UA-Compatible" content="IE=edge"><!--<![endif]-->`} />
    </head>
  )
}

The output will leave some empty script tags along the way which is not optimal but it works:

<html>
  <head>
    <title>Title</title>
    <script></script>
    <!--[if !mso]><!-->
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <!--<![endif]-->
    <script></script>
  </head>
  <body></body>
</html>

image

EDIT: Simplified usage a little bit, also if you're planning to use renderToStaticMarkup, you can do this to cleanup the empty scripts:

ReactDOMServer.renderToStaticMarkup(<MyRootComponent />)
  // Remove `<RawHtml />` injected scripts
  .replace(/<script><\/script>/g, "")

@denghongcai
Copy link

I found many opensource participants, but no one from React team with power to check if this RPC is acceptable or need to be upgrade?

I know there is no guarantee for RFC timeline, but it has been 2 years.

@frank-dspeed
Copy link

@denghongcai the problem is that it does not rely work with the core principles of how react works

react parses dom and or html strings into unfed react dom model so the way to create compat is to parse html to the react dom model to enable raw HTML

while out of dev view it is more simple to isolate react components into its own parts and not attach a parsed tree to the react part

maybe i should draw a model to make that more understand able.

<raw html />
<some_react />

never!

<raw html />
<some_react><raw html /></some_react>

i know this maybe gets downvotes because it is handy to take react apps that use the whole document and then modifie them but thats how react is that is not my opinion but the code that is needed to take raw html to react dom is a beast it self.

all existing solutions do parse the HTML to ReactDOM

@EthanStandel
Copy link

Obviously this is incredibly stalled, but is there any possibility of dangerouslySetOuterHTML on regular DOM elements in JSX? There's a good chance it would screw with the VDOM, but man I would just like to see any solution to get this issue chugging along.

@Kiwka
Copy link

Kiwka commented Sep 20, 2024

I would love to have this feature. I currently have a need to insert regular HTML comment in the React rendered application, that will be not removed during the rendering, and there is not easy way to do it. I can do workaround with document.createComment() and append it inside the node I need, but it removed and ease with which React allows us to define order of the children in the dom tree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.