• how to create a simple homepage that lists the pins created by different users
  • how to execute GraphQL queries using already configured Apollo Client
  • quick introduction to the @webiny/react-router library
Can I use this?

In order to follow this tutorial, you must use Webiny version 5.18.0 or greater.

The code that we cover in this section can also be found in our GitHub examples repository . Also, if you’d like to see the complete and final code of the application we’re building, check out the full-example folder.


Now that we have our New Pin modal dialog working as expected, we’re can continue by creating the homepage. As seen in previous sections, the homepage lists all pins that were created by different users.

Like in the previous section, fetching the list of created pins can be easily done via the Apollo Client and its useQuery React hook. A bit trickier part is rendering the pins in a mosaic layout, which is how the original Pinterest website does it.

Pinterest - The Mosaic LayoutPinterest - The Mosaic Layout
(click to enlarge)

Luckily, we can achieve this pretty easily with a small React library called react-columned , which we’ll need to add to our React application. Once we have that, we can start working on our homepage by adjusting the existing Home React component.

We already had a quick mention of the Home React component in the Optional Steps section, in the Layout section of this tutorial.

Creating the Homepage

For starters, let’s add the mentioned react-columned library with the following command:

yarn workspace pinterest-clone-app add react-columned

Once we have that, we can start working on our homepage. The following is the final Home React component that’s responsible for rendering it, located in the pinterest-clone/app/code/src/plugins/routes/home.tsx file:

import React from "react";
import { RoutePlugin } from "@webiny/app/plugins/RoutePlugin";
import { Link, Route } from "@webiny/react-router";
import { Empty } from "antd";
import { useQuery } from "@apollo/react-hooks";
import gql from "graphql-tag";
import Columned from "react-columned";
import Layout from "~/components/Layout";
import blankImage from "~/images/blankImage.png";

const LIST_PINS = gql`
    query ListPins($sort: PinsListSort, $limit: Int, $after: String, $before: String) {
        pins {
            listPins(sort: $sort, limit: $limit, after: $after, before: $before) {
                data {

// The home page.
const Home: React.FC = () => {
    const listPinsQuery = useQuery(LIST_PINS, { variables: { limit: 100 } });
    const { data = [] } = listPinsQuery?.data?.pins?.listPins || {};

    return (
        <Layout className={"home"}>
            {data.length > 0 ? (
                /* If we have pins to show, use the `Columned` component to render them in a mosaic layout. */
                    {data.map(item => (
                        /* Every pin should link to its details page. */
                        <Link key={item.id} to={"/pins/" + item.id}>
                            {/* If the pin contains an image, we show it. Otherwise, we show a placeholder image. */}
                                src={item.coverImage || blankImage}
            ) : (
                /* If there are no pins to show, render "Nothing to show." message. */
                <Empty description={"Nothing to show."} />

// We register routes via the `RoutePlugin` plugin.
export default new RoutePlugin({
    route: <Route path="/" exact component={Home} />

In order for the above code to work, make sure to copy and paste the blankImage.png file into the pinterest-clone/app/code/src/images folder.

Notice how we’ve used the tilde (~) character to target the parent images and components folders. Using that instead of something like ../../../ can make our import statements easier to read and maintain.

As we can see, the file contains the Home React component, in which we’re simply issuing the ListPins GraphQL query, and then, based on the received data, rendering each pin as an image and link to the Pin Details page (more on this soon). And, in order to achieve the mentioned mosaic layout, the pins are wrapped with the Columned component, imported from the added react-columned library.

Furthermore, note that, by default, the React application that’s generated during the Full Stack Application scaffolding process uses @webiny/react-router library, which is a plugins-based React application router. In other words, all of the application routes are defined via RoutePlugin plugins, which is why we’re exporting that instead of the actual Home React component:

// We register routes via the `RoutePlugin` plugin.
export default new RoutePlugin({
    route: <Route path="/" exact component={Home} />

All of these plugins are then registered in the pinterest-clone/app/code/src/plugins/index.ts plugins entrypoint file. For example, in case of the Homepage, we have the following:

import { plugins } from "@webiny/plugins";import apolloLinkPlugins from "./apollo";import home from "./routes/home";import notFound from "./routes/notFound";
// Imports and registers all defined plugins.plugins.register([  // Various Apollo client plugins.  apolloLinkPlugins,
  // Application routes.  home,  notFound]);

At the moment, this file already includes all of the necessary imports. No further changes need to be made.

The @webiny/react-router library is a thin wrapper around the popular React Router and its react-router-dom library, which just adds a couple of minor features to it. For example, via the ReactRouterOnLinkPlugin plugin, it gives developers the ability to execute a piece of code for every link that’s rendered on the page.

Additional Page Styles

One last thing before we move on. Let’s replace the code in the existing home.scss file with the following:

// Home page styles.
.home {
    img {
      vertical-align: middle;
      border-style: none;
      width: 100%;
      border-radius: 20px;
      padding: 5px;

Not super important, but making this change this will make the pins listed on our homepage look a little bit nicer.

Final Result

With this component in place, our homepage should look like the following:

Homepage - List of PinsHomepage - List of Pins
(click to enlarge)

At the moment, we could probably say that our homepage looks a bit bland and maybe boring. But we don’t need to worry about that for now. The whole page will start looking much more lively as soon as we bring the missing cover image field and file upload functionality into the mix.

For now, our goal was to have our homepage show a list of pins created by different users, and we’ve certainly achieved that. This means we’re ready for the final step, and that’s creating the Pin Details page.