Is it feasible to allow this as an option for this package?
Something like the following:
<ReactMarkdown source={input} linkTargets="_blank"/>
A bit unsure. It's easy enough to add, but I don't know if the API makes sense. I think there are two options:
Link: MyCustomLinkComponent. This would be very flexible and is something I've been wanting to add anyway.linkTarget prop that can either be a string or a function. The functions could determine the target based on the node, for instance whether it is a relative URL or not.I'm leaning towards the first, as that will allow all sorts of flexibility to be added without bloating the renderer with options.
I agree. The first option you listed sounds the best way to go and would open up the API to be much more flexible.
Do you have any inkling as to when such a feature could be ready?
I don't think it should be too much work, so it's more a question of when I'll have the time to prioritize it. I'm hoping it'll be this week, at least.
That would be perfect. I really like this package as I don't have to use the dangerouslySetInnerHTML property!
Keep up the good work on this package :+1:
Wouldn't the most optimal case be that any valid attribute would be valid and applied? When rendering the container, you could just spread the props with ...this.props (and handling special cases such as source like you do now).
Then you could just do <ReactMarkdown containerTagName="a" target="_blank" href="https://foo.com" source={"Link **here** to visit foo.com"}>
I've implemented custom renderers in commonmark-react-renderer (which powers this component). I'll see if I can get a new release of react-markdown out quite soon. When that is out it should be as as easy as:
<ReactMarkdown
source="Click [here](https://espen.codes/) to visit external site"
renderers={{Link: props => <a href={props.href} target="_blank">{props.children</a>}}
/>
Version 4 is now out, and the above example should work for this use case.
I wonder how to do a paragraph like this? I end up Each child in an array or iterator should have a unique "key" prop. Check the render method of 'Paragraph'
This seems to occur when using the softBreak: 'br' option.
Update - can be fixed by overriding the softBreak renderer with const SoftBreak = () => (<br/>)
In case what I found helps others:
I needed something similar within an Electron app and went with this solution before I found this issue:
var shell = require('electron').shell;
//open links externally by default
$(document).on('click', 'a[href^="http"]', function(event) {
event.preventDefault();
shell.openExternal(this.href);
});
...though this makes _all_ links on the page get opened by the user's default browser, not just links rendered by react-markdown, which might not quite be what you're looking for.
shouldn't it be
<ReactMarkdown
source="Click [here](https://espen.codes/) to visit external site"
renderers={{Link: props => <a href={props.href} target="_blank">{props.children}</a>}}/>
Dont forget to add rel="noopener noreferrer"
(See https://medium.com/@jitbit/target-blank-the-most-underestimated-vulnerability-ever-96e328301f4c)
I'm currently using it like so:
<ReactMarkdown
renderers={{ Link: (props: Object) => {
if (props.href.match('http')) {
return <a href={props.href} target="_blank" rel="nofollow noreferrer noopener">{props.children}</a>
}
return <a href={props.href}>{props.children}</a>
} }}
maybe this should be built in?
or passable as a prop?
so we could do something like ...
<ReactMarkdown externalLinksInNewTab />
Whats the reason you have if (props.href.match('http')) { check?
What else can it be?
Never mind. Just realised that there are a plethora of protocols. That said, I would not recommend blindly constructing a link. A safer option would be:
const renderLink = (props: Object) => {
if (!props.href.startsWith('http')) {
return props.children;
}
return <a href={props.href} rel='nofollow noreferrer noopener' target='_blank'>{props.children}</a>;
};
@gajus
that will not work, because [my internal link](/some-internal-path/) will just render my internal link as text
Sorry, this should have been props.href, i.e.
const renderLink = (props: Object) => {
if (!props.href.startsWith('http')) {
return props.href;
}
return <a href={props.href} rel='nofollow noreferrer noopener' target='_blank'>{props.children}</a>;
};
And yes, that would just render plain text link.
@gajus
yes but we want to render an internal link
oh, I see. Forgot about that possibility.
having a [legitimate link](http://www.example.com//){name:value} look easy enough where the {...} is a json with properties added to the link tag. and ignore it if there is a space between ] and { . or maybe go with the {:target="_blank"}
i'd prefer the json solution.
For anyone wondering, in version 4.0.6 the Link should be lowercase:
<ReactMarkdown
source={source}
renderers={{link: props => <a href={props.href} target="_blank">{props.children}</a>}}
/>
Most helpful comment
For anyone wondering, in version 4.0.6 the
Linkshould be lowercase: