packages/html-elements/README.md
Simple, light-weight, and well tested, universal semantic HTML elements as React components for iOS, Android, web, and desktop apps!
We at Expo recommend using platform agnostic primitives like View, Image, and Text whenever possible but sometimes that's not easy. Some primitives like Tables, and Footers are native to web only and currently have no way of easily accessing. This package aims to solve that while still being an optimal UI package for iOS, and Android.
@expo/html-elements will optimize your website for SEO and accessibility. Meaning your websites are indexed more accurately and your native apps better accommodate physically impaired users.
react-native-web a11y rules whenever possible.H1 component will render an <h1 /> on web, a UILabel on iOS, and a TextView on Android.StyleSheet API.href on a Text element.Install:
yarn add @expo/html-elements
Import and use the package:
import { H1 } from '@expo/html-elements';
Here is a list of all the currently supported elements and the web feature they map to. Not all HTML elements are supported. There are some HTML elements that mostly overlap with some universal modules, you should always try to use the universal modules whenever possible. All supported components are a capitalized variation of the semantic HTML they implement/emulate.
Other features not implemented in this package can be found in different parts of the Expo ecosystem.
| HTML | Universal | Package |
|---|---|---|
<audio /> | Audio | expo-av |
<button /> | <Button /> | react-native |
<input type="text" /> | <TextInput /> | react-native |
<input type="file" /> | ImagePicker | expo-image-picker |
<input type="file" /> | DocumentPicker | expo-document-picker |
<canvas /> | <GLView /> | expo-gl & Expo Canvas |
<iframe /> | <WebView /> | <WebView />. @react-native-community/web-view is not maintained by Expo and doesn't have web support |
<link /> | None | Eject the index.html with npx expo customize and link resources directly with <link /> |
<noscript /> | None | Eject the index.html with npx expo customize and use <noscript /> directly as HTML |
<div /> | <View /> | react-native |
| `` | <Image /> | react-native |
<span /> | <Text /> | react-native |
<video /> | <Video /> | expo-av |
style="backdrop-filter" | <BlurView /> | expo-blur |
style="linear-gradient()" | <LinearGradient /> | expo-linear-gradient |
Header elements will use the expected font size and margins from web universally. You can see how the native CSS units (rem, and em) are transformed in css/units.
import { H1, H2, H3, H4, H5, H6 } from '@expo/html-elements';
<H1/>import { H1 } from '@expo/html-elements';
export default () => <H1>Example<H1/>
| Platform | Output |
|---|---|
| Web | <h1 aria-level="1" dir="auto" role="heading" /> |
| Native | <Text role="header" style={[Custom]} /> |
<H2/>import { H2 } from '@expo/html-elements';
export default () => <H2>Example<H2/>
| Platform | Output |
|---|---|
| Web | <h2 aria-level="2" dir="auto" role="heading" /> |
| Native | <Text role="header" style={[Custom]} /> |
<H3/>import { H3 } from '@expo/html-elements';
export default () => <H3>Example<H3/>
| Platform | Output |
|---|---|
| Web | <h3 aria-level="3" dir="auto" role="heading" /> |
| Native | <Text role="header" style={[Custom]} /> |
<H4/>import { H4 } from '@expo/html-elements';
export default () => <H4>Example<H4/>
| Platform | Output |
|---|---|
| Web | <h4 aria-level="4" dir="auto" role="heading" /> |
| Native | <Text role="header" style={[Custom]} /> |
<H5/>import { H5 } from '@expo/html-elements';
export default () => <H5>Example<H5/>
| Platform | Output |
|---|---|
| Web | <h5 aria-level="5" dir="auto" role="heading" /> |
| Native | <Text role="header" style={[Custom]} /> |
<H6/>import { H6 } from '@expo/html-elements';
export default () => <H6>Example<H6/>
| Platform | Output |
|---|---|
| Web | <h6 aria-level="6" dir="auto" role="heading" /> |
| Native | <Text role="header" style={[Custom]} /> |
<A/>You can use the anchor element with href prop to open links. On native this will attempt to use the Linking API to open the href.
<Text />react-native-web-hooks | tutorialimport { A } from '@expo/html-elements';
export default () => <A href="#" target="_blank" />;
}
| Platform | Output |
|---|---|
| Web | <a data-focusable="{true}" dir="auto" href="#" role="link" target="_blank" /> |
| Native | <Text role="link" onPress={[Function]} /> |
You can use layout elements like Header, Main, Footer, Section, Nav, etc. as a drop-in replacement for Views in your existing app.
All layout HTML elements inherit the shared style of <View /> to accommodate the Yoga layout engine which we use on native for iOS, and Android.
display is always flex. This is because Yoga only implements display: flex.flex-direction is always column instead of row.Consider the following: in your app you have a basic element at the top which wraps the buttons and title. A screen reader doesn't understand that this is a header, and mostly neither does a web crawler. But if you replace the encasing view with a <Header /> the following happens:
UIView uses UIAccessibilityTraitHeader.View will use the proper AccessibilityNodeInfoCompat.CollectionItemInfoCompat | docs.<header /> with the ARIA role set to "banner".Some elements like Footer and Main have no iOS, or Android enhancements, but they'll still improve web. Using the proper HTML 5 elements will make your layout compliant with the HTML5 outline algorithm.
<Div/>Renders a <div /> on web and a View with no ARIA set on mobile.
import { Div } from '@expo/html-elements';
export default () => (
<Div>
<P>Some content in the main element</P>
</Div>
);
)
| Platform | Output |
|---|---|
| Web | <div /> |
| Native | <View /> |
<Nav/>import { Nav } from '@expo/html-elements';
export default () => <Nav />;
| Platform | Output |
|---|---|
| Web | <nav style="display:flex" /> |
| Native | <View /> |
<Header/>Renders a <header /> on web with ARIA set to banner and a View with ARIA set to header on mobile.
import { Header } from '@expo/html-elements';
export default () => <Header />;
| Platform | Output |
|---|---|
| Web | <header role="banner" /> |
| Native | <View /> |
| iOS | UIView uses UIAccessibilityTraitHeader. |
| Android | View will use the proper AccessibilityNodeInfoCompat.CollectionItemInfoCompat docs |
<Main/>Renders a <main /> on web with ARIA role set to main and a View with no ARIA set on mobile.
import { Main } from '@expo/html-elements';
export default () => (
<Main>
<P>Some content in the main element</P>
</Main>
);
)
| Platform | Output |
|---|---|
| Web | <main role="main" style="display:flex" /> |
| Native | <View /> |
<Section/>Renders a <section /> on web with ARIA set to region and a View with ARIA set to summary on mobile.
import { Section } from '@expo/html-elements';
export default () => <Section />;
| Platform | Output |
|---|---|
| Web | <section role="region" style="display:flex" /> |
| Native | <View role="summary" /> |
<Article/>Renders an <article /> on web and a View everywhere else.
import { Article } from '@expo/html-elements';
export default () => <Article />;
| Platform | Output |
|---|---|
| Web | <article role="article" /> |
| Native | <View /> |
<Aside/>import { Aside } from '@expo/html-elements';
export default () => <Aside />;
| Platform | Output |
|---|---|
| Web | <aside role="complementary" /> |
| Native | <View /> |
<Footer/>Renders an <footer /> on web and a View everywhere else.
import { Footer } from '@expo/html-elements';
export default () => <Footer />;
| Platform | Output |
|---|---|
| Web | <footer role="contentinfo" /> |
| Native | <View /> |
Text elements currently use Text universally rendering either a div or span to emulate Yoga style properly.
Code, and Pre).StyleSheet API.import { P, B, S, I, BR, Code } from '@expo/html-elements';
export default () => (
<>
<P>
Hello<B>World (in bold)</B>
</P>
<S>strike text</S>
<I>Italic</I>
<Code>const foo = true</Code>
</>
);
<P/>Standard paragraph element.
| Platform | Output |
|---|---|
| Universal | <Text style={{ fontSize: 14, marginVertical: '1em' }} /> |
<B/>Bold text text.
| Platform | Output |
|---|---|
| Universal | <Text style={{ fontWeight: 'bold' }} /> |
<Strong/>Alternate bold text.
| Platform | Output |
|---|---|
| Universal | <Text style={{ fontWeight: 'bold' }} /> |
<Span/>Inline text element.
| Platform | Output |
|---|---|
| Universal | <Text /> |
<S/>Strike through text.
| Platform | Output |
|---|---|
| Universal | <Text style={{ textDecorationLine: 'line-through' }} /> |
<Del/>Alternate strike through text.
| Platform | Output |
|---|---|
| Universal | <Text style={{ textDecorationLine: 'line-through' }} /> |
<I/>Italic text.
| Platform | Output |
|---|---|
| Universal | <Text style={{ fontStyle: 'italic' }} /> |
<EM/>Alternate italic text.
| Platform | Output |
|---|---|
| Universal | <Text style={{ fontStyle: 'italic' }} /> |
<Code/>Inline code block with fontFamily: 'Courier' on iOS and Web, fontFamily: 'monospace' on Android.
| Platform | Output |
|---|---|
| Universal | <Text style={[Custom]} /> |
<Pre/>Render a preformatted code block with fontFamily: 'Courier' on iOS and Web, fontFamily: 'monospace' on Android.
<Pre>{`
body {
color: red;
}
`}</Pre>
// Or pass views
<Pre>
<Code>{`const val = true`}</Code>
</Pre>
| Platform | Output |
|---|---|
| Universal | <Text style={[Custom]} /> | <View /> |
<Mark/>Highlight text.
| Platform | Output |
|---|---|
| Universal | <Text style={{ backgroundColor: 'yellow', color: 'black' }} /> |
<Q/>Quoted text.
| Platform | Output |
|---|---|
| Universal | <Text style={[Custom]}>"{props.children}"</Text> |
<BlockQuote/>| Platform | Output |
|---|---|
| Universal | <View style={[Custom]} /> |
<Time/>dateTime prop is supported on web and stripped on native.| Platform | Output |
|---|---|
| Universal | <Text style={[Custom]} /> |
Lists can be used to create basic bulleted or numbered lists. You should try and use universal FlatList or SectionList components for long scrolling lists instead of these.
<UL/>Create an unordered (bulleted) list <ul /> on web, and emulates the style with a <View /> on native.
import { UL, LI } from '@expo/html-elements';
export default () => (
<UL>
<LI>oranges</LI>
<LI>apples</LI>
<UL>
<LI>green</LI>
<LI>red</LI>
</UL>
</UL>
);
| Platform | Output |
|---|---|
| Web | <ul /> |
| Native | <View style={[Custom]} /> |
<LI/>Create a standard list item <li /> on web and a native view on mobile which can render text or views inside it.
| Platform | Output |
|---|---|
| Web | <li /> |
| Native | <Text style={[Custom]} /> | <View style={[Custom]} /> |
<HR/>Renders a <View> everywhere. Style is modified to match web.
import { HR } from '@expo/html-elements';
export default () => <HR />;
| Platform | Output |
|---|---|
| Web | <hr /> |
| Native | <View style={[Custom]} /> |
`
Create a line break.
| Platform | Output |
|---|---|
| Web | ` |
| ` | |
| Native | <View style={{height: 8, width: 0}} /> |
Create tables universally.
padding is removed from all table elements.TH and TD on mobile.colSpan and rowSpan are currently web-only (PRs welcome).import { Table, THead, TH, TBody, TFoot, TR, TD, Caption } from '@expo/html-elements';
import { Text } from 'react-native';
export default () => (
<Table>
<Caption>Caption</Caption>
<THead>
<TR>
<TH colSpan="2">The table header</TH>
</TR>
</THead>
<TBody>
<TR>
<TD>The table body</TD>
<TD>with two columns</TD>
</TR>
</TBody>
<TFoot>
<TR>
<TD>
<Text>This is the table footer</Text>
</TD>
</TR>
</TFoot>
</Table>
);
<table>
<caption>
Caption
</caption>
<thead>
<tr>
<th colspan="2">The table header</th>
</tr>
</thead>
<tbody>
<tr>
<td>The table body</td>
<td>with two columns</td>
</tr>
</tbody>
<tfoot>
<tr>
<td><div>The table body</div></td>
</tr>
</tfoot>
</table>
<Table/>Base element for creating a Table.
| Platform | Output |
|---|---|
| Web | <table /> |
| Native | <View style={[Custom]} /> |
<THead/>Header element in a Table.
| Platform | Output |
|---|---|
| Web | <thead /> |
| Native | <View style={[Custom]} /> |
<TBody/>Body element in a Table.
| Platform | Output |
|---|---|
| Web | <tbody /> |
| Native | <View style={[Custom]} /> |
<TFoot/>Footer element in a Table.
| Platform | Output |
|---|---|
| Web | <tfoot /> |
| Native | <View style={[Custom]} /> |
<TH/>Used to display text in the Header.
colSpan and rowSpan are currently web-only.| Platform | Output |
|---|---|
| Web | <th /> |
| Native | <Text style={[Custom]} /> |
<TR/>Used to create a Row in a Table.
| Platform | Output |
|---|---|
| Web | <tr /> |
| Native | <View style={[Custom]} /> |
<TD/>Create a cell in a Table.
colSpan and rowSpan are currently web-only.| Platform | Output |
|---|---|
| Web | <td /> |
| Native | <View style={[Custom]} /> |
<Caption/>Used to caption your table. Excepts text as a child.
| Platform | Output |
|---|---|
| Web | <caption /> |
| Native | <Text style={[Custom]} /> |
You can write react-dom elements in your code and use the babel plugin to transform them to @expo/html-elements elements.
// babel.config.js
module.exports = {
plugins: ['@expo/html-elements/babel'],
};
export default function Page() {
return (
<div>
<h1>Hello World</h1>
</div>
);
}
The import is automatically added if it's not already present. All props are passed through without any additional transforms.
import { Div, H1 } from '@expo/html-elements';
export default function Page() {
return (
<Div>
<H1>Hello World</H1>
</Div>
);
}
Contributions are very welcome! Please refer to guidelines described in the contributing guide.
<!-- HTML element links -->