Skip to main content

How to migrate from MVC to React

In this How-To, we are using the same Sample MVC website as we used in our Jamstack for Sitecore MVC Tutorial.

There are several steps needed to start rewriting components in React.

Before you start#

We are going to rewrite ImageTextBlock component from the front page of the Uniform MVC sample app. First, let's save outer html from inspect element in notepad. We are going to use it later.

/img/how-to-migrate-mvc-to-react/Untitled.png

This is the html in case if you've missed this step for some reason:

<div class="row pb-5">    <div class="col-md-8"><img            src="https://sc93.sc/-/media/project/uniform-mvc-kit/waves.ashx?h=480&amp;iar=0&amp;w=720&amp;hash=15DB7893AEEA90AE6394DD0F9B21F985"            class="card-img-top img-fluid" alt="waves" width="720" height="480"></div>    <div class="col-md-4">        <h3 class="my-3">Project Description</h3>        <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam viverra euismod odio, gravida pellentesque urna            varius vitae. Sed dui lorem, adipiscing in adipiscing et, interdum nec metus. Mauris ultricies, justo eu            convallis placerat, felis enim.</p>        <h3 class="my-3">Project Details</h3>        <ul>            <li>Lorem Ipsum</li>            <li>Dolor Sit Amet</li>            <li>Consectetur</li>            <li>Adipiscing Elit</li>        </ul>    </div></div>

Step 2. Create a simple component in Uniform Next.js App#

  1. In .env file in the root, set

    UNIFORM_OPTIONS_MVC_MODE=mixed

    This setting is used in Uniform Service to understand, which data to request from Sitecore for rendering. Without mixed mode, only map.json and page.json services are requested. With mixed mode set, for each component we'll also request item.json, which allows us to rewrite components in React, and get current datasource item data through props

  2. Create components folder in the root:

    /img/how-to-migrate-mvc-to-react/Untitled1.png

  3. In components folder, create ImageWithText.jsx file, and fill it with the next contents:

    export const ImageWithText = (props) => {       return (        <div>I'm a new ImageWithText component!</div>    )}
  4. Now we need to tell our app where the new component is. Find pages/[[..slug]].tsx file:

    /img/how-to-migrate-mvc-to-react/Untitled2.png

    Add the next lines right after defining of componentsIndex const:

    import { ImageWithText } from "../components/ImageWithText";componentsIndex.ImageTextBlock = ImageWithText;

    This is how your slug.tsx is going to look like now:

    /img/how-to-migrate-mvc-to-react/Untitled3.png

  5. Now, try to run yarn dev and load localhost:3000. This is what you should see:

    /img/how-to-migrate-mvc-to-react/Untitled4.png

    Note that the MVC component has been replaced with our new simplistic React component.

Congrats! Now you've rewritten your MVC component in React!

Now, let's try to make this component to look exactly like MVC component we had before.

Step 3. Make the new React component look like before#

  1. Take the html code from step 1, and run it through any html to jss converter - there are several of them available as extensions in Visual Studio Code, for example. The resulting code should look like this:

                <div className="row pb-5">            <div className="col-md-8">                <img src="https://sc93.sc/-/media/project/uniform-mvc-kit/waves.ashx?h=480&iar=0&w=720&hash=15DB7893AEEA90AE6394DD0F9B21F985" className="card-img-top img-fluid" alt="waves" width={720} height={480} />            </div>            <div className="col-md-4">                <h3 className="my-3">Project Description</h3>                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam viverra euismod odio, gravida pellentesque urna varius vitae. Sed dui lorem, adipiscing in adipiscing et, interdum nec metus. Mauris ultricies, justo eu convallis placerat, felis enim.</p>                <h3 className="my-3">Project Details</h3>                <ul>                    <li>Lorem Ipsum</li>                    <li>Dolor Sit Amet</li>                    <li>Consectetur</li>                    <li>Adipiscing Elit</li>                </ul>            </div>        </div>
  2. Replace our <div>I'm a new ImageWithText component!</div> in ImageWithText.jsx file with this code. Now ImageWithText.jsx should look like this:

    export const ImageWithText = (props) => {    return (        <div className="row pb-5">            <div className="col-md-8">                <img src="https://sc93.sc/-/media/project/uniform-mvc-kit/waves.ashx?h=480&iar=0&w=720&hash=15DB7893AEEA90AE6394DD0F9B21F985" className="card-img-top img-fluid" alt="waves" width={720} height={480} />            </div>            <div className="col-md-4">                <h3 className="my-3">Project Description</h3>                <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam viverra euismod odio, gravida pellentesque urna varius vitae. Sed dui lorem, adipiscing in adipiscing et, interdum nec metus. Mauris ultricies, justo eu convallis placerat, felis enim.</p>                <h3 className="my-3">Project Details</h3>                <ul>                    <li>Lorem Ipsum</li>                    <li>Dolor Sit Amet</li>                    <li>Consectetur</li>                    <li>Adipiscing Elit</li>                </ul>            </div>        </div>    )}
  3. Switch over to your browser. If you are still running yarn dev, then your content on localhost:3000 should already be replaced and our component should look exactly like when it was rendered through MVC:

    /img/how-to-migrate-mvc-to-react/Untitled5.png

Congrats! now our new React component looks exactly like the former MVC component. We are getting closer and closer to the fully React presentation!

Step 4. Pass and use props in a new React component#

However, as you might have noticed, our new component now contains only hardcoded texts and images. How do we make it to represent Sitecore fields?

Step 4.1 Sitecore Configuration for getting rich component props#

Our new component props contain an object called renderingContext, that is being formed from page.json returned by Uniform Connector. So to tell our page.json to contain specific datasource information, needed for ImageWithText component, we need somehow to tell our Sitecore side that this particular rendering will need to pass data for it's datasources.

This can be done in two ways. For both of them we need to know our rendering id first. Maybe you already know which rendering it is - so just save its id somewhere. If you can't pinpoint the exact rendering corresponding to the part of the page you want to rewrite, you can look into page.json API output and look for the content from that place on the page:

/img/how-to-migrate-mvc-to-react/Untitled6.png

ef4ad74fe1ac443288ced69aaea2bec0 is the Id of the actual Sitecore rendering

Now, when you know ID of the rendering, either use a config setting to override it (Option 1) or change rendering's item template (Option 2). If you use a config setting, you will still be able to use MVC rendering on your Sitecore side - for example, for Experience Editor. When you change template of the rendering, you won't see this rendering in Experience Editor anymore.

Option 1. Adding a config setting#

1) Activate uniform-mvc-kit.Uniform.Deployment.ReactRenderings.config in App_Config\Include\zzz_uniform-mvc-kit by removing .disabled extension from it.

2) Instead of sampleRenderingId being overridden, add our rendering id, so that your config file now looks like this:

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/">  <sitecore>    <uniform>      <services>        <renderingNodeService>          <javaScriptRenderingOverrides>            <ImageTextBlock>{EF4AD74F-E1AC-4432-88CE-D69AAEA2BEC0}</ImageTextBlock>                   </javaScriptRenderingOverrides>        </renderingNodeService>      </services>     </uniform>  </sitecore></configuration>

Option 2. Changing template of the rendering#

1) Find rendering definition item by rendering id in Sitecore:

/img/how-to-migrate-mvc-to-react/Untitled7.png

2) Change template Change template of the rendering we are planning to rewrite to Uniform Javascript Rendering:

/img/how-to-migrate-mvc-to-react/Untitled8.png

/img/how-to-migrate-mvc-to-react/Untitled9.png

You are going to get warning about fields that will miss after change - just ignore it.

After template change your rendering item will look like this:

/img/how-to-migrate-mvc-to-react/Untitled10.png

Important: now you need to still have your Build service up and running with yarn start or yarn dev. We need to keep it running now, because we will need to publish this rendering item.

4) Publish rendering item.

Step 4.2 Getting data in the component#

Since UFS 7.2 it is available to get data in a component in two ways:

Configure the component model#

To configure the component model need to set the rendering id for the pageModelServiceand register a model builder for the rendering datasource in the componentModelBuilderService. The model builder should have componentId and parameterName attributes:

  • componentId is a rendering ID or camel-cased rendering name. Example: rendering name Image Text Block -> imageTextBlock
  • parameterName is a component property name. Use datasource when you need to build model from rendering datasource or page item, if datasource is not specified, or use camel-cased parameter name for item-based rendering parameters. (e.g. Media Item converts to mediaItem)
<configuration  xmlns:set="http://www.sitecore.net/xmlconfig/set/"  xmlns:patch="http://www.sitecore.net/xmlconfig/">
  <sitecore>    <uniform>      <siteConfigurations>        <siteConfiguration name="uniform-mvc-kit">          <componentModel>            <pageModelService ref="uniform/services/pageModelService">              <renderings hint="list:RegisterRenderingId">                <ImageTextBlock>{EF4AD74F-E1AC-4432-88CE-D69AAEA2BEC0}</ImageTextBlock>              </renderings>            </pageModelService>            <componentModelBuilderService ref="uniform/services/componentModelBuilderService">              <modelBuilders hint="raw:RegisterModelBuilder">                <imageTextBlock componentId="{EF4AD74F-E1AC-4432-88CE-D69AAEA2BEC0}" parameterName="datasource" type="Uniform.ModelBuilders.ItemFieldsModelBuilder, Uniform.ComponentModel">                  <Templates hint="list:AddTemplate">                    <ImageTextBlock>{B1A05AE5-6854-4358-B336-316A514C592F}</ImageTextBlock>                  </Templates>                  <Fields hint="raw:AddField">                    <field>Text</field>                    <field>Image</field>                  </Fields>                </imageTextBlock>              </modelBuilders>            </componentModelBuilderService>          </componentModel>        </siteConfiguration>      </siteConfigurations>    </uniform>  </sitecore></configuration>

To check that model configured visit page.json and find models, it contains the model for overridden rendering:

/img/how-to-migrate-mvc-to-react/Untitled11.png

info

Detailed documentation on how to use Model Builders

Getting the component model in the component#

When the component model configurations are done, we can use the model as a part of props. To finishe the compoentn migration process we need to update the ImageWithText.jsx. Here is the final code for ImageWithText:

export const ImageWithText = ({datasource}) => {       return (        <div className="row pb-5">            <div className="col-md-8">                <img src={datasource.image.url} className="card-img-top img-fluid" alt="waves" width={720} height={480} />            </div>            <div className="col-md-4">                <div dangerouslySetInnerHTML={{ __html: datasource.text }} />                           </div>        </div>    )}
info

Although we need to use dangerouslySetInnerHtml for Rich Text fields, otherwise you'll get all the <h3>, <ul> and <li> tags from Rich Text as is on the page.

Congrats! now you've successfully overridden MVC component with React representation, followed the same style and retrieved field values from rendering's datasource!

Getting datasource context data in the component [Deprecated since UFS 7.2]#

info

To enable the feature use Uniform.Data.Page.LegacyReactDatasources.Enabled setting

To check that step #4.1 is done correctly visit page.json and find datasources, it contains the datasource for overridden rendering:

/img/how-to-migrate-mvc-to-react/Untitled11.png

With the change above, fields from datasource item, specified for the rendering, will now be propagated to props of the component that we are rewriting. More specifically, datasource became a part of the renderingContext object in props, and can be accessed now via props.renderingContext.item.fields in that component. Let's check it!

  1. If you try to read renderingContext object from props in ImageWithText component, you should get everything that is available on the page at the moment:
export const ImageWithText = (props) => {    const { renderingContext } = props;    console.log({ renderingContext });}
  1. Check your console in browser where you have localhost:3000 running and observe the renderingContext:

/img/how-to-migrate-mvc-to-react/Untitled12.png

  1. Now we can see our datasource item fields being passed as a part of props!

    This is the point where our component becomes fully functional. The only thing we need to do now is to replace hardcoded parts of the ImageWithText.jsx with the data available in props:

    • our image source will become {renderingContext.item.fields.image.url}
    • our text will become {renderingContext.item.fields.text}

Although we need to use dangerouslySetInnerHtml for Rich Text fields, otherwise you'll get all the <h3>, <ul> and <li> tags from Rich Text as is on the page.

Here is the final code for ImageWithText:

export const ImageWithText = (props) => {    const { renderingContext } = props;    const { item } = renderingContext;    const { fields } = item;          return (        <div className="row pb-5">            <div className="col-md-8">                <img src={fields.image.url} className="card-img-top img-fluid" alt="waves" width={720} height={480} />            </div>            <div className="col-md-4">                <div dangerouslySetInnerHTML={{ __html: fields.text }} />                           </div>        </div>    )}