Modular components
This topic is subject to change as the progress of venia-ui implementation need to be updated!
The Venia storefront is a React application composed of multiple React components. Some of these components come from third-party dependencies, and the rest come from Peregrine or defined in the Venia project itself.
The design of the Venia project makes it possible to use its component outside the Venia storefront. This lets you leverage Venia functionality in your own PWA projects.
Prerequisites
Node 12 deprecation warning
If you are using Node 12, you may see the following deprecation warning in the log when you run yarn watch:venia.
(node:89176) [DEP0066] DeprecationWarning: OutgoingMessage.prototype._headers is deprecated
This is caused by a project dependency used by PWA Studio and not by PWA Studio itself.
Install package dependency
Use Yarn to install the @magento/venia-concept package:
yarn add @magento/venia-concept
Import Venia components
Individual Venia components are imported from the src directory of the Venia package.
import VeniaProductDetail from '@magento/venia-concept/src/components/ProductFullDetail'; import Product from '@magento/venia-concept/src/RootComponents/Product';
These components are defined in the project as ES Modules to help with Webpack optimization.
Venia drivers and adapter
Simple components, such as LoadingIndicator and RichText, can be plugged into your code, and they will work correctly without modifications.
Complex components, such as ProductFullDetail and CategoryList, use objects with external dependencies, such as Query and Link.
To use complex components in your own project, you have the following options:
- Import the Venia Adapter and wrap it around Venia components
- Override src/drivers so its components do not depend on context and IO provided by an adapter
Venia Adapter
Import and use the Venia Adapter in your project if your storefront already uses Apollo and React Redux
import VeniaAdapter from '@magento/venia-concept/src/drivers/adapter'; import { createStore } from 'redux'; import { ApolloClient } from '@apollo/client/core'; const myApplicationStore = createStore() const myClient = new ApolloClient({ uri: "https://mystore.com/graphql"}) function App () => ( <VeniaAdapter client={myClient} store={myApplicationStore} apiBase="https://mystore.com"> // Use Venia components here </VeniaAdapter> )
The Venia Adapter wraps around Venia components to satisfy any implicit external dependency it has, such as a GraphQL client or Redux store.
Venia Adapter props
Prop name |
Type |
Description |
client |
Apollo Client |
Client object to pass on to an ApolloProvider component |
store |
Redux Store |
The application store to pass on to a Redux Provider component |
apiBase |
string |
Root URL of the Magento store to use in the Peregrine Router component |
Venia drivers
The src/drivers dependency is a centralized module for Venia components that rely on external dependencies, such as GraphQL clients and Redux stores. Instead of importing these dependencies directly, Venia components import them from the virtual dependency @magento/venia-drivers.
import { Link, resourceUrl } from '@magento/venia-drivers';
The @magento/venia-drivers dependency is not listed in package.json or available on the NPM registry. Instead, this works because of the following configuration in venia-ui/package.json:
"browser": { "@magento/venia-drivers": "src/drivers" }
Webpack treats this package.json configuration as equivalent to a Webpack alias configuration, as required by this draft specification. An app which imports anything from @magento/venia-ui will substitute the virtual dependency for the real file at build time. In your app, you can override the implementation of src/drivers and the "browser" field which aliases it, by specifying a Webpack alias as described below.
The default implementation, which is used in the Venia storefront, provides modules that work with the components provided by the Venia Adapter.
Module name |
Source |
Link |
react-router-dom |
Redirect |
react-router-dom |
Route |
react-router-dom |
resourceUrl |
makeUrl.js |
Adapter |
adapter.js |
connect |
react-redux |
Custom drivers
You can create a custom implementation of the src/drivers module to instead provide custom drivers for Venia components. This lets you use Venia components without wrapping them inside a Venia Adapter.
Use a build tool, such as Webpack or Rollup, to override driver module imports in Venia components.
Example webpack.config.js:
module: { alias: { "@magento/venia-drivers": "./myReplacementDrivers" } }
Example ./myReplacementDrivers:
import React, { Component } from 'react'; import { resourceUrl as veniaResourceUrl } from '@magento/venia-concept/src/drivers'; // A replacement Query that loads forever export class Query extends Component { render() { return this.props.children({ loading: true }); } } // A replacement Link that doesn't use the client-side router export class Link extends Component { render() { const { children, to, ...other } = this.props; return ( <a {...other} href={to}> {children} </a> ); } } // A replacement resourceUrl that calls Venia's implementation by importing // Venia's default driver, then additionally validates urls and adds a parameter export function resourceUrl(...args) { let url = veniaResourceUrl(...args); try { url = new window.URL(url); } catch (e) { url = new window.URL(url, window.location.origin); } const params = new URLSearchParams(url.search); params.append('referrer', window.location.hostname); url.search = `?${params}`; return url.href; } // You can also override Router, Route, Redirect, and react-redux's // `connect()` HOC
The examples provided creates an import or require alias for @magento/venia-drivers and have it resolve to myReplacementDrivers.
This means that any module that imports from src/drivers will import from myReplacementDrivers instead of the default @magento/venia-drivers.
Example project
See the venia-consumer-example project to see how a non-Venia application can import and use Venia components using this approach.