React render raw HTML as-is partially inside element - javascript

I need to add some raw HTML to my <head> to my React/Next.js app in addition to already-existing elements, something like below:
<head>
<existingElements.../>
[I WANT TO INSERT WHATEVER ARBITRARY HTML HERE]
</head>
If I use dangerouslySetInnerHTML I lose the existing elements.
I can't use dangerouslySetInnerHTML as <Head> in Next.js doesn't have that property at all (and I'm trying to add to the Head element).
If I try using node-html-parser or react-html-parser:
import {parse} from 'node-html-parser';
[...]
let additionalElements:any = <></>;
additionalElements = parse(MY_ARBITRARY_HTML_STRING); // no parse errors here
[...]
return <head>{additionalElements}</head>
They end up erroring with `Error: Objects are not valid as a React child (my parsed HTML here).
I've thought there was a problem with the HTML that I provide but I get the same error even if I try providing something simple as <b>hello</b>.
How can I insert arbitrary HTML string into the head element while keeping existing head elements too, using React and Next.js?
(Note: I'm fully aware of XSS and security implications, this is an internally/strictly controlled HTML.)

Related

Is it safe to use dangerouslySetInnerHTML with hard coded html strings?

We have an alert component that renders important information for the user. However, this component has somewhat an abstraction where you just need to pass the content as an array of string.
const Component = () => {
const alertContent = ['This is the first things', 'Second thing', 'third thing'];
return (
<AlertComponent content={alertContent} />
)
}
This successfully displays the content as a list. However, we need to bolden parts of the text for emphasis. I thought about changing the strings in the array to include html tags, then use dangerouslySetInnerHTML to render them. Something like:
const alertContent = ['This is an <b>important</b> text', ...];
return (
<>
{alertContent.map(content => <span dangerouslySetInnerHTML={{__html: content}} />)}
</>
)
I've read about cross-site scripting attacks, but most articles I've read talk about user inputs and third party APIs. Does hard-coding the html prevent this? Or do I still need to use sanitizers?
If you, the coder, create all of the the HTML to be inserted, and are sure that it doesn't have any XSS vulnerabilities, then yes, it'll be safe.
dangerouslySetInnerHTML is named as such to tell you primarily that, if used incorrectly, it's very easy to open your app up to security problems. But if the HTML that gets set is hard-coded and safe, then dangerouslySetInnerHTML is safe too.
Sanitizers are necessary when the markup comes from user input, or from an external service. They're not needed if the markup comes entirely from your own code.
That said, in this particular situation:
However, we need to bolden parts of the text for emphasis
Why not just use JSX instead?
const alertContent = [<>This is an <b>important</b> text</>, ...];
Writing in JSX when possible is much preferable to having to resort to dangerouslySetInnerHTML.
If a text to be rendered in dangerouslySetInnerHTML is set by administrators or proved users, it should be safe and no problem.
But if normal users can set the texts or html contents, you should consider it.
You can provide html UI editors such as TinyMCE for users to set html content and implement some logic to strip the dangerous html tags such as iframe, script , etc on your backend apps.

Is it possible to render a PHP frontend into a Vue node?

I have a legacy PHP app which I would like to slowly migrate to Vue. The PHP app renders a bunch of HTML and javascript files in quite a tangled fashion, i.e.
foo.js.php
...
<script src="mysite.com/some_js_file.js" />
...
const a = '<?=$variable_from_php?>';
so in the end, the browser obviously doesn't know how the js files are constructed, but can run them. What I'd like to do is from the outer layer Vue app, request the index page for a certain sub-section of the legacy app, and render that to a Vue node, as a micro-frontend of sorts. When I request each index, it will of course, contain a header with numerous other imports (scripts/styles) that that micro-frontend needs to function. So, two parts to this question: 1) what would be the best (or maybe least terrible) way to do this in Vue. Using v-html? iframe? (please say no iframes) And 2) will there be any showstopper security problems with this approach (since I'm basically saying fetch all the JS in the header and run it). Let me know if this question makes sense. Thanks!
Maybe you need like to : a module php or component as template.php(php server)
export const templateOfAdvanceTemplatePage = `
<div class="content edit-page management">
<md-card class="page-card">
<?php echo "My Component" ?>
</md-card>
</div>
And from node server
import * as url from "url";
var templateOfAdvanceTemplatePage = url.parse("http://www.website.com/template.php");
export default {
template: templateOfAdvanceTemplatePage,
...
}
for more information import vue here, and php as javascript file here
Vue.js can be used in two separate ways: For more complex applications you would use a build process and pre-compile the templates from the source, which are usually Single File Components SFC; *.vue files. The templates would then become render functions and no HTML is ending up in the output assets. There is, however, another way of defining Vue components. You can define them inline with the runtime-only bundle of Vue. For migrations and smaller applications this approach would be advised. You would need to include the compiler. See also the Vue documentation about that topic Vue v2 and Vue v3). If you are importing Vue as a module and are missing the compiler, see here.
If you want to render dynamically generated HTML from PHP as a Vue template, you would need the second approach. Keep in mind that, with this approach, you would always need to have the generated PHP output to be in sync with the Vue components. And you would need to fully trust the HTML, you are generating with PHP, otherwise you will risk injections.
There is, however, still another problem: You need the generated PHP output HTML as a string within JavaScript and it should not be interpreted by the browser (ideally) or removed again from the DOM. So, you need to decide (based on your project) how you want to generate the HTML so that it can be read in as a JavaScript string. Here are some approaches:
Generate the HTML directly into the page. Then, define which element you want to target, get the HTML with .innerHTML and delete the node from HTML (drawback: you will render the HTML twice, might produce short visual glitches).
Fetch the HTML via XHR from a separate page. You will directly have the HTML as a string in the response (see e.g. fetch).
Render <script type="text/x-template" id="static-html-content"></script> around the generated HTML content. Then, you do not need the HTML as string and you can directly use the id as reference (use template: '#static-html-content'). See the documentation of X-Templates in Vue.
Then, you can use the runtime-only version of Vue and define your components. Here is a live example:
const Counter = {
// retrieve and add your template string here
template: `
<div class="counter">
This is a counter: {{ counter }}
<button #click="counter++">Increase Counter</button>
</div>
`,
data: function() {
return {
counter: 0
}
}
};
const App = {
components: { Counter },
template: `
<div class="app">
This is the app component.
<hr />
<counter />
</div>
`
};
new Vue({
el: '#element',
template: '<App />',
components: { App }
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="element"></div>
Another approach would be to just render the HTML string within a component with the v-html attribute. The main drawback of this solution is, however, that the content is then not reactive. You cannot change your internal component data and expect the template to react to the changes. Therefore, you are missing out on the main benefits of Vue, but you are not restricted to a template which matches your components internal structure.
A similar question was also posed in the Vue forum: link

JavaScript API to create Svelte elements instead of a template

Svelte’s templating language is great, in that it looks like HTML. However, for highly dynamic content I need to be able to use the full power of JavaScript, not just #if and #each. For example, given a tree data structure, I want to generate hierarchical table headings. (Demo) In React most apps use JSX templates, but you can drop down to createElement if you need to. Is there a similar path for Svelte? Am I missing something obvious?
If you need access to the DOM node, you can:
Add bind:this={node} to get access to the DOM node:
<script>
import {onMount} from 'svelte'
let node
onMount(() => {
const dynamic = document.createElement('a')
dynamic.innerHTML = "Click me!"
node.appendChild(dynamic)
})
</script>
<div bind:this={node}/>
Add a use directive, this will also get you access to the raw DOM node
<script>
function ninja(node) {
node.innerHTML = "Kawabunga!"
}
</script>
<div use:ninja/>
I would look at the <svelte:self> element, which allows you to create elements that call themselves recursively.
https://svelte.dev/tutorial/svelte-self

Using Markdown-to-JSX package to convert markup to HTML inside a React component's render function

I am pulling JSON data from an API. The JSON contains blog posts from social media site Steemit.com. The posts are in markup and I want to grab the markup from the API, convert it to regular HTML, and display it in a React component.
Here is my render function. The API data is called in componentDidMount and consists of deeply nested objects. Excuse the messiness.
render() {
const posts = Object.keys(this.state.myPosts);
let page = posts.map((post, i) => {
return (
this.state.myPosts[posts[i]]["category"] === "utopian-io"
?
<div className="content-container">
<p>
<strong>Author: {this.state.myPosts[posts[i]]["author"]}</strong>
</p>
<p>
<strong>Title: {this.state.myPosts[posts[i]]["title"]}</strong>
</p>
<p>
<strong>Payout: {this.state.myPosts[posts[i]]["total_payout_value"]}</strong>
</p>
<p>{this.state.myPosts[posts[i]]["created"]}</p>
<p><span>Category: </span>{this.state.myPosts[posts[i]]["category"]}</p>
// Successfully renders the markup, but I want to convert this to HTML so it displays as HTML
<p>{this.state.myPosts[posts[i]]["body"]}</p>
// I have tried the following but I get a "not a string" console.error or the browser just crashes
<Markdown>{this.state.myPosts[posts[i]]["body"]}</Markdown>
</div>
:
null
);
});
return (
<div>
{page}
</div>
);
}
}
The Markdown-to-JSX package is successfully imported and calling it with the <Markdown></Markdown> tags is the appropriate way to use it. The problem is it expects a string and when I pass it this.state.myPosts[posts[i]]["body"] it won't work. I have tried to convert the data to a string using toString() and JSON.stringify() but I am unable to figure this out.
How do I pass the JSON markup data from the API to Markdown-to-JSX as a string?
In order words, I think converting this.state.myPosts[posts[i]]["body"] to a string should fix this but I don't know how to do this.
Option 1: dangerouslySetInnerHTML
If you would like to inject the HTML in the body of the post, use dangerouslySetInnerHTML
render: function() {
return (
<div dangerouslySetInnerHTML={{__html: this.state.myPosts[posts[i]]["body"]}></div>
);
}
You need to use dangerouslySetInnerHTML because React escapes the HTML to prevent XSS ( a security stuff to avoid injecting malicious code )
If you transform the post body to string directly, you will add in the body a lot of html tags as string, not recommended either.
If you parse the markup, and would like to convert it, its a heavy task.
In general, the practice for this is that the API call wouldn't return you a formatted markup.
Option 2: npm-library
https://www.npmjs.com/package/html-to-react
You shouldn't loose the magic of React. So this probably is a nice option to convert your post patterns into React 'Post Body' Components.
Your result should be having all inside components. In this way you are keeping the benefits of React

RiotJS - How to pass events between subtags using Observable pattern?

Im not really sure if Im understanding correctly the way observables work and how to get references from mounted tags. I have a component. Within this component we have a component and a component. The purpose is to avoid coupling between components. Because of that, I would like that my search component triggers an event when a search is done(a button is clicked). This event should be caught by the component which will filter the collection data based on the search.
The index.html file load the tag by using:
index.html
riot.mount(".content", "page", null);
The page is defined as follow:
page.js
<page>
<!-- Search tag controls -->
<search id="searchTag"></search>
<!-- Collection data to display -->
<collection id="collectionTag"></collection>
</page>
The component script is briefly defined like:
search.js
var self = this;
riot.observable(self);
<!-- This function is called when the user click on the button. -->
self.filtering = function()
{
<!-- We get data from inputs -->
var info = Getting data from inputs;
<!-- Trigger the event hoping that someone will observe it -->
self.trigger("filterEvent", info);
}
How can I make the component observe for that event?
To me it seems that I should be able to get references from search tag and collection tag in the page.js. By doing so I could connect the events like follow:
searchComponent = riot.mount('search');
collectionComponent = riot.mount('collection');
searchComponent.on('filterEvent', function()
{
<!-- Trigger function to filter collection data -->
collectionComponent.trigger('filterData');
});
Right now I cannot make it work like that.
At the point of execution, searchComponent and collectionComponent are not defined.
I tried also getting references of these component by using this.searchTag and this.collectionTag instead of mounting them but at the time the code is executed, the components have not been mounted and so I dont get a reference to them.
Any ideas to make it work?
Inspired by the answer given by #gius, this is now my preferred method for sending events in RiotJS from one tag to another.. and it is great to work with!
The difference from #gius approach being that, if you use a lot of nested tags, passing a shared Observable to each tag falls short, because you would need to pass it again and again to each child tag (or call up from the child tags with messy this.parent calls).
Defining a simple Mixin, like this (below), that simply defines an Observable, means that you can now share that in any tag you want.
var SharedMixin = {
observable: riot.observable()
};
Add this line to your tags..
this.mixin(SharedMixin);
And now, any tag that contains the above line can fire events like..
this.observable.trigger('event_of_mine');
..or receive events like this..
this.observable.on('event_of_mine',doSomeStuff());
See my working jsfiddle here http://jsfiddle.net/3b32yqb1/5/ .
Try to pass a shared observable to both tags.
var sharedObservable = riot.observable();
riot.mount('search', {observable: sharedObservable}); // the second argument will be used as opts
riot.mount('collection', {observable: sharedObservable});
And then in the tags, just use it:
this.opts.observable.trigger('myEvent');
this.opts.observable.on('myEvent', function() { ... });
EDIT:
Or even better, since your search and collection tags are child tags of another riot tag (page) (and thus you also don't need to mount them manually), you can use the parent as the shared observable. So just trigger or handle events in your child tags like this:
this.parent.trigger('myEvent');
this.parent.on('myEvent', function() { ... });
Firstly I do not understand your file structure !
In your place I would change filenames :
page.js --> page.tag
search.js --> search.tag
And i dont see your search tag in search.js code.
So I dont see your Collection tag file ...
Are you sure that this one use this code ?
riot.observable({self|this});
Because it's him who will receive an Event.
For me when I use Riot.js(2.2.2) in my browser, if I use
searchComponent = riot.mount('search');
searchComponent will be undefined
But with this code you can save your monted tag reference :
var searchComponent ={};
riot.compile(function() {
searchComponent = riot.mount('search')[0];
});
Another option is to use global observables, which is probably not always best practice. We use Riot's built in conditionals to mount tags when certain conditions are met rather than directly mounting them via JS. This means tags are independent of each other.
For example, a single observable could be used to manage all communication. This isn't a useful example on its own, it's just to demonstrate a technique.
For example, in a plain JS file such as main.js:
var myApp = riot.observable();
One tag file may trigger an update.
var self = this;
message = self.message;
myApp.trigger('NewMessage', message);
Any number of other tag files can listen for an update:
myApp.on('NewMessage', function(message) {
// Do something with the new message "message"
console.log('Message received: ' + message);
});
Maybe overkill but simple. let riot self observable
riot.observable(riot);
So you can use
riot.on('someEvent', () => {
// doing something
});
in a tag, and
riot.trigger('someEvent');
in another.
It's not good to use global variable, but use an already exists one maybe acceptable.

Categories