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 render a page, listing available props. These props are fetched by the 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 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 app to reflect this.
  1. Add query by creating the following file in your Next application

    src/components/queries/getPerson.ts
    import {APP_NAME_UNDERSCORED} from '../../_enonicAdapter/utils'
    
    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. Register the query by adding the following lines to the component mappings file:

    Do not copy the …​ - it is just there to illustrate there are other lines of code you should keep in the target file.
    src/components/_mappings.ts
    import getPerson from './queries/getPerson';
    
    ...
    
    ComponentRegistry.addContentType(`${APP_NAME}:person`, {
        query: getPerson,
        view: PropsView
    });
  3. View 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 custom person rendering

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

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

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

    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
    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 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";
  2. And replace this line:

    return <Component {...pageProps} />;

    with this:

    return (
    <>
      <Header
        title="🔥 Next.XP"
        logoUrl={getUrl('images/xp-shield.svg')}/>
        <main style={{
          margin: `0 auto`,
          maxWidth: 960,
          padding: `0 1rem`,
          }}>
          <Component {...pageProps} />
        </main>
      <Footer/>
    </>
    );
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 next chapter, well add preview support in Content Studio.


Contents