react2amp v5.0.2
react2amp
Easy to migrate exist React App (build by Webpack, babel and Express) to AMP website. both support react@16 and react@17
Thanks @savemuse contrubutes to add support for webpack@5 for webpack@4 users, please use @react2amp@1 for webpack@5 users, please use @react2amp@5
Features
- Collect AMP component tags and generate script tag to load these resources,
- Support customized AMP component version specification like:
<amp-carousel data-ver="0.2">{...}</amp-carousel> - Collect CSS and generate style tag with
amp-customattribute. - Provide
useAmp()to distinguish between AMP and non-AMP pages.
Installation
npm inatall --save react2ampModules
Html
React component which render whole AMP HTML includes all necessary tags meets AMP HTML specification
props type required default description head ReactElement[] false [] Array of React.Elementtags(such as title, link and meta)main ReactNode true - Main content of application asset Asset false {} Map of AMP components and css data styles ReactElement[] false [] Array of React.Elementstyles to support Css-In-Js solutions (styled-jsx and styled-components)// Asset type type Asset = { [entry]: { scripts: { name: string, version: number } css: string } , ... }usage:
import { renderToString } from 'react-dom/server' import { Html } from 'react2amp' renderToString(<Html {...} />)AmpProvider
React context provider to indicate components (
children) that current page is AMPusage:
import React from 'react' import { AmpProvider } from 'react2amp' function Text() { return useAmp() ? "This is Amp page" : "This is non-Amp page" } function AmpApp() { return ( <AmpProvider> /** `useAmp() will return true` */ <Text /> </AmpProvider> ) }useAmp Distinguish between AMP and non-AMP pages.
useAmp()will return true insideAmpProviderusage:
// Image.js import React from 'react' import {useAmp} from 'react2amp' function Image() { return ( { useAmp() ? ( <amp-img {...} /> ) : ( <img {...} /> ) } ) }webpackPluginAmpAssets
Webpack plugin to collect AMP component tags and css by each entry
props type required default description filename string true - The file to write the assets to includeEntries string[] false [] Only collect assets from the entries inside includeEntriesexcludeJsResourcesRegExp RegExp false - Ignore the module resources match the excludeJsResourcesRegExprules when finding AMP componentsexcludeCssResourcesRegExp RegExp false - Ignore the module resources match the excludeCssResourcesRegExprules when finding cssusage:
// webpack.config.js const AmpAssetsPlugin = require('react2amp/plugin').webpackPluginAmpAssets module.exports = { entry: { entry1: './components/App1.js', entry2: './components/App2.js', entry3: './components/App3.js' }, ... plugins: [ ..., new AmpAssetsPlugin({ filename: `${build_path}/react2ampAsset.json`, includeEntries: ['entry1', 'entry3'], excludeResourcesRegExp: /node_modules\/(@babel|lodash|core-js|@apollo|graphql|react|react-dom)\/.*/ }) ] }The
react2ampAsset.jsonwould look like{ "entry1": { "scripts": [ { "name": "amp-form", "version": 0.1 }, { "name": "amp-bind", "version": 0.1 } ], "css": "html{line-height:1.15;}body{margin:0}" }, "entry3": { "scripts": [ { "name": "amp-animation", "version": 0.1 }, { "name": "amp-carousel", "version": 0.2 } ], "css": "html{line-height:1.15;}body{margin:0}" } }babelPluginAmpClassName
Babel plugin to transpile
classNameprop of AMP tags toclassprop. React will renderclassNameprop of custom HTML tag (AMP component) asclasslike this:<amp-img className="image">=><amp classname="image">, check here for more informatin.usage:
// babel.config.js const babelPluginAmpClassName = require('react2amp/plugin').babelPluginAmpClassName module.exports = { presets: [ ... ], plugins: [ ..., babelPluginAmpClassName ] }Or simply use amp-react-components to render AMP components, which map AMP component's
clsssNameprop toclassprop.expressAmpMiddleware
Express middleware to response AMP page to client side.
props type required default description head ReactElement[] false [] Array of React.Elementtags(such as title, link and meta)main ReactNode true - Main content of application asset Asset false {} Map of AMP components and css data styles ReactElement[] false [] Array of React.Elementstyles to support Css-In-Js solutions (styled-jsx and styled-components)usage:
import { expressAmpMiddleware } from 'react2amp' app.get('/amp/example', expressAmpMiddleware({...}))getAmpAsset
get assets by entry from JSON file generate by
webpackPluginAmpAssetsprops type required default description entry string true '' webpack entry assetFile string true '' assets file usage:
import { getAmpAsset } from 'react2amp/plugins' const entry = 'entry1' const assetFilename = `../build/react2ampAsset.json` const asset = getAmpAsset(entry, assetFilename)
Example
Head.js render title, description and meta tags into HTML head tag. Render canonical at
AMP page and amphtml at original website.
// Head.js
import {useAmp} from 'react2amp'
function Head(){
return (
<>
<title>Title</title>
<meta name="description" content="Description" />
{
useAmp() ? (
/** react2amp will add meta for charset and viewport for AMP pages */
<link rel="canonical" href="https://www.react2amp.com/example" />
) : (
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,minimum-scale=1 initial-scale=1" />
<link rel="amphtml" href="https://www.react2amp.com/amp/example" />
)
}
</>
)
}App.js render the main application content. Render AMP component(amp-img) at
AMP page and img tag at original website.
// App.js
import {useAmp} from 'react2amp'
function App(){
const props = {...}
return (
<div>
{
useAmp() ? (
<amp-img {...props} layout="responsive" />
) : (
<img {...props} />
)
}
</div>
)
}Add babelPluginAmpClassName babel plugin to babel.config.js for transpiling className prop to class prop.
// babel.config.js
const babelPluginAmpClassName = require('react2amp/plugin').babelPluginAmpClassName
module.exports = {
presets: [
...
],
plugins: [
...,
babelPluginAmpClassName
]
}Add AmpAssetsPlugin webpack plugin to webpack.config.js for collecting AMP component tags and css in by entry point.
// webpack.config.js
const AmpAssetsPlugin = require('react2amp/plugin').webpackPluginAmpAssets
module.exports = {
entry: {
example: './components/App.js'
},
...
plugins: [
...,
new AmpAssetsPlugin({
filename: `${build_path}/react2ampAsset.json`
})
]
}Add expressAmpMiddleware express middleware to serve AMP page. And use AmpProvider(work with useAmp()) if we need to render component earlier to get styles or content with data in server side.
// express.js
import { renderToStaticMarkup, renderToString } from 'react-dom/server'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'
import { AmpProvider } from 'react2amp'
import { expressAmpMiddleware, getAmpAsset } from 'react2amp/plugins'
/** build by webpack AmpAssetsPlugin */
import assets from 'react2ampAsset.json'
import Head from './components/Head'
import App from './components/App'
app.get('/example', (req, res) => {
...
res.status(200)
res.send(`<!doctype html>\n${renderToStaticMarkup(html)}`)
res.end()
})
const entry = 'example'
const assetFilename = `${build_path}/react2ampAsset.json`
app.get('/amp/example', expressAmpMiddleware({
head: <Head />,
main: <App />,
asset: getAmpAsset(entry, assetFilename)
}))
/** with styled component */
const sheet = new ServerStyleSheet()
try {
const main = renderToString(
<AmpProvider>
<StyleSheetManager sheet={sheet.instance}>
<App />
</StyleSheetManager>
</AmpProvider>
)
const styles = sheet.getStyleElement()
app.get('/amp/styled/example', expressAmpMiddleware({
head: <Head />,
main,
asset: getAmpAsset(entry, assetFilename),
styles
}))
} catch (error) {
console.error(error)
} finally {
sheet.seal()
}Todo
- Update README.md
- develop mode
- Add examples
- react app with css module
- react app with styled component
- react app with apollo graphql
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago