Rendering basics

Contents

In this chapter we will look into the basic rendering mechanisms, and add some custom rendering to our app as well.

Dev mode rendering

As you have seen during the setup of Next, each content item automatically map to a page listing available props. These props are fetched by the so-called Enonic Adapter.

By defining custom queries and react components tailored for each content type, we can customise the props passed to the view, and which React template is actually applied.

Task: Get person content

Let’s build our first component - starting with rendring of "person" content types:

The full name of your content type should now be com.example.myproject:person If you used a different name when creating the Enonic app, update the .env file in your Next.js app to reflect this.
  1. Add query by creating the following file in your front-end application

    src/components/queries/getPerson.ts
    import {APP_NAME_UNDERSCORED} from '@enonic/nextjs-adapter';
    
    const getPerson = `
    query($path:ID!){
      guillotine {
        get(key:$path) {
          displayName
          ... on ${APP_NAME_UNDERSCORED}_Person {
            data {
              bio
              dateofbirth
              photos {
               ... on media_Image {
                  imageUrl: imageUrl(type: absolute, scale: "width(500)")
                  attachments {
                    name
                  }
                }
              }
            }
          }
          parent {
            _path(type: siteRelative)
          }
        }
      }
    }`;
    
    export default getPerson;

    This file defines a graphQL query 'getPerson' that will fetch data from the Enonic API.

    You may validate the query by copying it to the GraphQL playground. Replace APP_NAME_UNDERSCORED with com_example_myproject and set a query variable, try this for instance: {"path": "/hmdb/persons/brad-pitt"}`
  2. Add the following imports to component mappings file:

    src/components/_mappings.ts
    import getPerson from './queries/getPerson';
    import {APP_NAME} from '@enonic/nextjs-adapter';
  3. Then Register the query in the same file:

    src/components/_mappings.ts
    // Content type mappings
    ComponentRegistry.addContentType(`${APP_NAME}:person`, {
        query: getPerson,
        view: PropsView
    });
  4. Check the result

    By visiting a person item - like http://localhost:3000/persons/morgan-freeman you should now see data props being listed in the view.

    morgan freeman props

Task: Add person template

With fresh content available, it’s time to make things shine:

  1. Add view by creating the following file in your Next.js application

    src/components/views/Person.tsx
    import React from 'react'
    import {FetchContentResult, getUrl} from '@enonic/nextjs-adapter';
    
    const Person = (props: FetchContentResult) => {
        const {displayName, data, parent} = props.data?.get as any;
        const {bio, photos} = data;
        const meta = props.meta;
        const {_path} = parent;
    
        return (
            <>
                <div>
                    <h2>{displayName}</h2>
                    <p>{bio}</p>
                    {
                        photos.map((photo: any, i: number) => (
                            <img key={i}
                                 src={getUrl(photo.imageUrl, meta)}
                                 title={getTitle(photo, displayName)}
                                 alt={getTitle(photo, displayName)}
                                 width="500"
                            />
                        ))
                    }
                </div>
                <p><a href={getUrl(_path, meta)}>Back to Persons</a></p>
            </>
        )
    }
    
    export default Person;
    
    function getTitle(photo: any, displayName: string) {
        return (photo.attachments || [])[0].name || displayName;
    }

    This file defines a react view Person that will use the props from the query to render the person.

  2. Register Person view by adding the following import to the component mappings:

    src/components/_mappings.ts
    import Person from './views/Person';

    And then update the component mapping you created earlier to use the new view:

    src/components/_mappings.ts
    // Content type mappings
    ComponentRegistry.addContentType(`${APP_NAME}:person`, {
        query: getPerson,
        view: Person
    });
  3. Check the result - The page should automatically reload once the changes have been saved

    morgan freeman render

The content type mapping applies to every person content item in the site, regardless of it’s location in the hierarchy. Try looking at some other persons, eg. http://localhost:3000/persons/uma-thurman

Adding some styles, a header and a footer will make the site more interesting visually. Next.js provides a handy solution to this problem.

The _app.tsx file initializes the React pipeline, and by modifying it we can inject the same header and footer components to all pages generated by Next.

Sample header and footer views are already included in the starter under /src/components/views, so all we need to do is put them to work.

Update src/pages/_app.tsx` with the following changes:

  1. Add these imports

    import Header from '../components/views/Header';
    import Footer from '../components/views/Footer';
    import {getUrl} from '@enonic/nextjs-adapter';
  2. And at the bottom of the file replace the entire return function:

    return (
        <StaticContent condition={isEdit}>
            <Component {...pageProps} />
        </StaticContent>
    );

    with this:

    return (
        <StaticContent condition={isEdit}>
            <Header
                title="🔥 Next.XP"
                logoUrl={getUrl('/images/xp-shield.svg', pageProps.meta)}
                meta={pageProps.meta}
            />
                <main style={{
                    margin: `0 auto`,
                    maxWidth: 960,
                    padding: `0 1rem`,
                }}>
                    <Component {...pageProps} />
                </main>
            <Footer/>
        </StaticContent>
    );
Pay attention to the getUrl() function. It ensures that links to assets and pages are working seamlessly in Next.js, as well as in Content Studio preview mode - as you will see later.

Morgan Freeman’s page should now look like this

morgan freeman header footer

The common query

You may have noticed the Common props appearing in the various prop views - these originate from the the following statement in the mappings:

src/components/_mappings.ts
...

// Common query
ComponentRegistry.setCommonQuery([commonQuery, commonVariables]);

...

The result of this query is available across all components on the page, and may be useful if you want to fetch data for the page itself, or to be used across multiple components. Feel free to adapt it to your requirements.

If you do not need any common props, remove it from the mappings to optimize performance.

With the basic rendering completed, in the following chapter, you’ll add preview support to Content Studio.


Contents

Contents