Rendering Scrivito Page and Widget Markup with JSX

Rendering Scrivito Page and Widget Markup with JSX

Writing or modifying Scrivito page or widget components is quite easy once you’ve mastered the particularities of React and JSX. After that, the full power of them is at your hands, allowing you to implement Scrivito-based apps more confidently and much faster.

This guide focuses on the rendering part of pages and widgets: React components. For getting a more general overview of what a page or widget consists of, take a look at our Introduction to Creating and Rendering Pages and Widgets.

Why JSX and how to use it

Roughly, JSX – “the” JavaScript eXtension – lets you handle markup as if it were code. Instead of using React.createElement() to generate markup, one can insert the markup into the code directly. Take a look at the return statement in this TextWidget component, which comes with the Scrivito Example App:

Without JSX, you would have to code the following:

Both snippets yield the same markup, of course. In the createElement version, null refers to the element’s children, none in this case.

Return a single element!

When creating elements using JSX, you might be tempted to return more than one element. Don’t do this, it doesn’t work.

Simply wrap the elements to return in a parent element such as a <div> as shown in the example above. However, if the parent element could lead to invalid markup (e.g. inside of a table), consider using fragments.

Technically, JSX converts markup to JavaScript for React. Isn’t this awesome? You could even omit the round brackets enclosing the markup because it’s turned into an expression: return <p>Hello world!</p>;

Accessing and rendering page and widget content

The Example App’s TextWidget component we started talking about above complements the Scrivito-specific widget class definition, which makes the attributes of the actual widgets known to the Scrivito SDK. The TextWidget class, for example, defines two attributes, text and alignment, that are taken care of at rendering time.

Like most widget and page components, the TextWidget component is created using Scrivito.provideComponent(). Behind the scenes, this Scrivito SDK function defines a stateless React component class for you that handles all data transfer related to its instances, asynchronously.

Whenever an instance of a Scrivito widget class is to be rendered, the function passed to Scrivito.provideComponent() is called. It receives a widget prop, making it a breeze to work with the instance data. Simply code widget.get('attribute_name') to retrieve the value of the specified attribute, like shown here for TextWidget instances:

The get() instance method of a widget or page lets you retrieve the value of an attribute. get() is used for attributes that either don’t contain content to be displayed (e.g. styling options like alignment above), or contain content to be rendered but not meant to be edited in place (e.g. form field labels). Such attributes are usually made editable via the properties dialog of a widget or page.

Making content editable in place

Attributes containing content you want to be editable in place need to be rendered using one of the built-in Scrivito helper components such as Scrivito.ContentTag. Here, in the component of the TextWidget, Scrivito.ContentTag renders a widget’s text attribute, activating the editing controls for working copies in edit mode:

Passing properties to a component

When passing properties to a component, make sure to take account of React-specific naming (e.g. className instead of class) or handling (style expects a JavaScript object, not a string); see the React DOM Elements documentation for details.

There’s two ways of specifying the value of a property passed to a component:

  • As a literal like with tag="div".
  • As a JavaScript expression (enclosed in curly braces) that gets evaluated, e.g.:
    className={ classNames.join(' ') } or
    className={ `bg-${widget.get('bgColor')}` }.
    The latter expression is a template string (recognizable by the enclosing pair of backticks), which allows you to have expressions evaluated inside of a literal (using ${…}). In the example above, the resulting string starts with “bg-” to which the value of the widget’s “bgColor” attribute is appended.

Embedding code in markup

When returning markup, JSX lets you use JavaScript code not only when passing properties to a component. You can also embed code in the parent element to be returned by a component in order to generate the children. Take a look at the following example in which an unordered list is generated from all Page CMS objects:

The code that iterates over the pages is enclosed in curly braces. It produces a list element for each page whose title is then linked to the respective page. Note that you can only use JavaScript expressions here; statements such as if and for, or assignments, are not allowed inside of JSX code, but you can use them before the return statement. See JSX in Depth for details.

Defining a component

The Scrivito.provideComponent() method makes it easy to equip a Scrivito widget or object class with rendering functionality.

When specifying a component with Scrivito.provideComponent(), you can access all of the widget or page instance’s attributes through the corresponding widget or, respectively, page prop Scrivito passes to the component, like with widget.get('alignment'), for example.

Components are also good for rendering Scrivito-related markup independently of a Scrivito widget or page class. Take a look at the AuthorImage component included in the Example App; it renders neither a Scrivito widget nor a page, so Scrivito.provideComponent cannot be applied.

There are a couple of things to note on this code:

  • The name of the function starts with a capital letter to indicate that it acts as a component and can be used like this, for example: <AuthorImage image={ author.get('image') }/>
  • You can accept a React props object as the function argument.
  • Scrivito.connect is used to have the component rerendered as the required data becomes available. (Remember that Scrivito loads all CMS object data asynchronously.)

A word about props

A component’s props argument is a JavaScript object passed at rendering time to a functional component or an instance of a component class. For example, if you render <PrettyIcon color='blue' />, you can access the color argument in the component code as a prop using props.color (in a functional component) to render the corresponding CSS class: