Creating your first Jahia component
Understanding the solid template structure
This template has the following structure:
It is composed of a header of a footer, and in between a succession of sections. We will create a content element for each section, and for some of these, we will also create sub-content elements.
For the sake of simplicity and not to make this tutorial too long, the header and the footer will not be modified.
For each content section, you’ll now need to:
- Identify the HTML block and its structure
- Create a content type definition in the corresponding component definition file
- Create a component view that will render the component content in HTML
Creating the content type definition
Let’s start with the hero section.
By looking at the section elements, we can see the elements we want to model, and we detail here what the above content type elements represent:
- The
solidTemplate:
before the type name is a namespace and is used to scope the definitions to our template set and will be defined in the header of the content definition (CND) file. - The content type definition is composed of properties, themselves composed of:
- a name
- a type
- optional parameters
- default values
- constraints
In this case, most of the properties will be of type string
and can be internationalized (i18n
), but we also use two of type weakreference
to build links to pages (jnt:page
). You can learn more about Jahia’s content type definitions.
Almost all the work that needs to be done is in the definitions.cnd
.
Add the following content to the file:
[solidTemplate:heroSection] > jnt:content, solidTemplatemix:solidTemplateComponents
- title (string) = 'Landing template for startups' i18n
- paragraph (string) = 'Our landing page template works on all devices, so you only have to set it up once, and get beautiful results forever.' i18n
- button1Text (string) = 'Pre order now' i18n
- button1Link (weakreference) < jnt:page
- button2Text (string) = 'Get in touch' i18n
- button2Link (weakreference) < jnt:page
We have also added default values, making testing our component a little faster. You can find more information about Jahia's content type definition here.
Adding a static view
Now that we have a model, we can also build the related view, meaning the JSX component that will generate the HTML for this particular content element.
Now we have to create our view so let’s create the associated view.
First, let’s create a sections
directory in $NPM_TEMPLATE/src/server/views
and then create the file:
$NPM_TEMPLATE/ src / server / views / heroSection / HeroSectionDefault.jsx
We add a Default
suffix to the nodetype in the file name to indicate that this is the default view for rendering this nodetype. A single nodetype may have different views, and the default one is used when no name is specified for the view.
And add this code (which will be the similar base code of all our views):
import React from 'react';
import {defineJahiaComponent} from "@jahia/js-server-core";
export const HeroSectionDefault = () => {
return (
);
}
HeroSectionDefault.jahiaComponent = defineJahiaComponent({ // this object is used to register the view in Jahia
nodeType: 'solidTemplate:heroSection', // The content node type the template applies to
displayName: 'Hero Section', // The display name of the view
componentType: 'view' // the component type is set to view (as opposed to template component types)
})
If we look at the HTML for the section in $NPM_TEMPLATE/src/server/templates/page/PageHome.jsx
, in lines 50 to 124, this section looks like this:
<section className="hero">
<div className="container">
<div className="hero-inner">
<div className="hero-copy">
<h1 className="hero-title mt-0">
Landing template for startups
</h1>
<p className="hero-paragraph">
Our landing page template works on all devices, so you only have to set it up once, and get beautiful results forever.
</p>
<div className="hero-cta">
<a
className="button button-primary"
href="#"
>
Pre order now
</a>
<a
className="button"
href="#"
>
Get in touch
</a>
</div>
</div>
<div className="hero-figure anime-element">
<svg
className="placeholder"
height="396"
viewBox="0 0 528 396"
width="528"
>
<rect
height="396"
style={{
fill: 'transparent'
}}
width="528"
/>
</svg>
<div
className="hero-figure-box hero-figure-box-01"
data-rotation="45deg"
/>
<div
className="hero-figure-box hero-figure-box-02"
data-rotation="-45deg"
/>
<div
className="hero-figure-box hero-figure-box-03"
data-rotation="0deg"
/>
<div
className="hero-figure-box hero-figure-box-04"
data-rotation="-135deg"
/>
<div className="hero-figure-box hero-figure-box-05" />
<div className="hero-figure-box hero-figure-box-06" />
<div className="hero-figure-box hero-figure-box-07" />
<div
className="hero-figure-box hero-figure-box-08"
data-rotation="-22deg"
/>
<div
className="hero-figure-box hero-figure-box-09"
data-rotation="-52deg"
/>
<div
className="hero-figure-box hero-figure-box-10"
data-rotation="-50deg"
/>
</div>
</div>
</div>
</section>
Copy and paste the above HTML section into the new view file return parenthesis.
Injecting content inside the view
We will now need to position the content object values in the view. Jahia provides a helper function called getNodeProps, that makes it easy to retrieve multiple properties of a node in a single call. So we can retrieve all the properties of our node using something like this:
const props = getNodeProps(currentNode, ['title', 'paragraph', 'button1Text', 'button1Link', 'button2Text', 'button2Link']);
We can then access the properties much simply by using their names, such as props.title
, or props.button1Link
Of course, we need some setup to have all the proper imports and variables.
For that, add the following import statement at the top of your code:
import {useServerContext, buildUrl, getNodeProps} from '@jahia/js-server-core';
And add the following at the beginning of the component function:
const {currentNode, renderContext, currentResource} = useServerContext();
const props = getNodeProps(currentNode, ['title', 'paragraph', 'button1Text', 'button1Link', 'button2Text', 'button2Link']);
Your code should now look like this:
import React from 'react';
import {useServerContext, buildUrl, getNodeProps} from '@jahia/js-server-core';
export const HeroSection = () => {
const {currentNode, renderContext, currentResource} = useServerContext();
const props = getNodeProps(currentNode, ['title', 'paragraph', 'button1Text', 'button1Link', 'button2Text', 'button2Link']);
return (
<section className="hero">
<div className="container">
<div className="hero-inner">
<div className="hero-copy">
...
We will now modify the view by inserting all the properties defined in the content definition to look like this:
<section className="hero">
<div className="container">
<div className="hero-inner">
<div className="hero-copy">
<h1 className="hero-title mt-0">{props.title}</h1>
<p className="hero-paragraph">{props.paragraph}</p>
<div className="hero-cta">
<a className="button button-primary" href={props.button1Link ? buildUrl({path:props.button1Link.getPath()}, renderContext, currentResource) : '#'}>{ props.button1Text }</a>
<a className="button" href={props.button2Link ? buildUrl({path:props.button2Link.getPath()}, renderContext, currentResource) : '#'}>{props.button2Text}</a>
</div>
</div>
<div className="hero-figure anime-element">
...content removed for brevity but should be included...
</div>
</div>
</div>
</section>
Note that for the page links, we had to do something a little more complex. As we need the path on the property, we must first ensure that it exists before generating the URL. This is done using a ternary function as in the following example:
<a className="button button-primary" href={props.button1Link ? buildUrl({path:props.button1Link.getPath()}) : '#'}>{ props.button1Text }</a>
Exporting the view and cleaning up the template
We now need to export the view, so create a src/server/views/heroSection/index.js
file with the following content:
export * from './HeroSectionDefault';
And to export it to the root of the src folder we need to add to the src/server/views/index.js
file with the following content:
export * from './heroSection';
We can now get rid of the whole <section>
for the heroSection in the src/server/templates/page/PageHome.jsx
file by removing the following content
<section className="hero">
... remove all children tags as well ...
</section>
Be careful to only remove the first section tag and children, we still need the others for the next tutorials.
Test our updates
Since we are using the “watch” system, simply reloading the Jahia Page Composer browser window will be sufficient to load all the changes.
Now when reloading the page in the Jahia Page Composer you will see a screen that looks like this:
You can now click the New content
button and add the Hero section by selecting it in the content type selection modal:
Select heroSection
and click CREATE
you will be shown the Content Editor screen for the heroSection definition that is prepopulated with the default values:
And if all went well, after click SAVE
and the back arrow button you should see your changes reflected in the page as illustrated below:
Doesn’t this look great?
Congratulations, you have created your first content definition and its associated view and used the Jahia Page composer UI to create an instance of your new object.
Next step
Continue to creating the Feature section