this is a small react renderer which allows you to render arbitrary XML using JSX.
import renderXML, { XML, xmlElement } from 'react-xml-renderer';
// you can create XML elements using the xmlElement helper:
const Foo = xmlElement('foo');
const fooBarJsx = <Foo asdf="123">bar</Foo>;
// or use the XML proxy instead:
const fooBarJsx2 = <XML.foo asdf="123">bar</XML.foo>;
// render your JSX to an xml string:
const fooBar = renderXML(fooBarJsx);
const fooBar2 = renderXml(fooBarJsx2);
console.assert(fooBar === fooBar2); // true
if you've ever thought of rendering XML with React, you might have realized you can use react-dom to render your JSX to a string instead of into a dom node:
import React from 'react';
import ReactDomServer from 'react-dom/server';
const jsx = <p>I'm a string!</p>;
const xml = ReactDomServer.renderToStaticMarkup(jsx);
console.log(xml); // <p>I'm a string!</p>
okay, cool. now let's start writing some non-html content like, say, an RSS feed:
import React from 'react';
import ReactDomServer from 'react-dom/server';
const jsx = <rss version="2.0"></rss>;
// ~~~~~~
// error TS2339: Property 'rss' does not exist on type 'JSX.IntrinsicElements'.
const xml = ReactDomServer.renderToStaticMarkup(jsx);
console.log(xml);
oops - typescript expects all lowercase components to be real HTML elements, as defined by JSX.IntrinsicElements
from @types/react
.
we can work around this by defining our own react component:
import React from 'react';
import ReactDomServer from 'react-dom/server';
const Rss = (props: Record<string, any>) => React.createElement('rss', props);
const jsx = <Rss version="2.0"></Rss>;
const xml = ReactDomServer.renderToStaticMarkup(jsx);
console.log(xml); // <rss version="2.0"></rss>
great, now we can build out the rest of our xml using this approach:
import React from 'react';
import ReactDomServer from 'react-dom/server';
const xmlElement = (name: string) => (props: Record<string, any>) =>
React.createElement(name, props);
const Rss = xmlElement('rss');
const Channel = xmlElement('channel');
const Title = xmlElement('title');
const Description = xmlElement('description');
const Link = xmlElement('link');
const Copyright = xmlElement('copyright');
const LastBuildDate = xmlElement('lastBuildDate');
const PubDate = xmlElement('pubDate');
const Ttl = xmlElement('ttl');
const Item = xmlElement('item');
const Guid = xmlElement('guid');
const jsx = (
<Rss version="2.0">
<Channel>
<>
<Title>RSS Title</Title>
<Description>This is an example of an RSS feed</Description>
<Link>http://www.example.com/main.html</Link>
<Copyright>2020 Example.com All rights reserved</Copyright>
<LastBuildDate>Mon, 06 Sep 2010 00:01:00 +0000 </LastBuildDate>
<PubDate>Sun, 06 Sep 2009 16:20:00 +0000</PubDate>
<Ttl>1800</Ttl>
<Item>
<Title>Example entry</Title>
<Description>
Here is some text containing an interesting Description.
</Description>
<Link>http://www.example.com/blog/post/1</Link>
<Guid isPermaLink="false">
7bd204c6-1655-4c27-aeee-53f933c5395f
</Guid>
<PubDate>Sun, 06 Sep 2009 16:20:00 +0000</PubDate>
</Item>
</>
</Channel>
</Rss>
);
const xml = ReactDomServer.renderToStaticMarkup(jsx);
console.log(xml);
this code doesn't trigger any typescript type errors! however, if we run it, we get a runtime error:
Error: link is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`.
<link>
, in HTML, is a void element, meaning it can't accept children.
this shouldn't matter to us, because we're not writing HTML, we're writing XML. but react-dom has special checks to prevent us from writing invalid HTML which can't be disabled. if we want to create non-HTML from JSX, we need a different renderer.
react-xml-renderer is a stripped-down fork of react-tiny-dom, and uses jsdom behind-the-scenes.