Building your first React app, part 1

In this tutorial, you'll use the Create React app boilerplate together with the JavaScript Delivery SDK to create a simple but functional blog. The blog will display articles from a Sample Project that any admin of a Kentico Kontent subscription can generate.

New to Kentico Kontent?

If you're starting with Kentico Kontent, we suggest you begin with the Hello World tutorial, in which you'll learn the basics of creating content.

Already completed part 1?

Go directly to Building your first React app, part 2, where you'll modify the app you made in part 1 to render embedded media, such as videos, and resolve links between content items.

Table of contents

    You will learn to:

    • Create a React app
    • Connect it to your Kentico Kontent project
    • Add components that render content from Kentico Kontent
    • Set up routing
    • Build and deploy your app

    You can view the final result here, see the source code on Github, or explore a live example without installing anything.

    Requirements

    You will need at least basic knowledge of npm, react, and react-router to follow this tutorial.

    Make sure you also have the following:

    1. Create react app

    Open your command line or terminal and run the following commands to set up a new React project. The create-react-app boilerplate will prepare and configure a development environment for you.

    • shell
    npx create-react-app my-kontent-react-app
    npx create-react-app my-kontent-react-app

    2. Install dependencies

    Navigate to the project folder and use npm to install all the required libraries:

    • shell
    cd my-kontent-react-app npm install --save react-router-dom rxjs @kentico/kontent-delivery
    cd my-kontent-react-app npm install --save react-router-dom rxjs @kentico/kontent-delivery
    • react-router-dom – for client-side routing in a single page application
    • rxjs – for managing asynchronous operations
    • @kentico/kontent-delivery – for retrieving content from Kentico Kontent

    3. Add components

    You will only add two components to your app – one for listing all articles and one for viewing a single article.

    Create an ArticleView.js file in the src folder:

    • JavaScript
    import React, { Component } from "react"; class ArticleView extends Component { render() { return ( <div> Article content </div> ); } } export default ArticleView;
    import React, { Component } from "react"; class ArticleView extends Component { render() { return ( <div> Article content </div> ); } } export default ArticleView;

    Create an ArticleListing.js file in the src folder:

    • JavaScript
    import React, { Component } from "react"; class ArticleListing extends Component { render() { return ( <div> Listing of articles </div> ); } } export default ArticleListing;
    import React, { Component } from "react"; class ArticleListing extends Component { render() { return ( <div> Listing of articles </div> ); } } export default ArticleListing;

    4. Add routing

    In the src folder, rewrite the App.js file with the following to create routes for ArticleView and ArticleListing components:

    • JavaScript
    import React, { Component } from "react"; import "./App.css"; // Imports routing dependencies from react-router-dom and our components import { BrowserRouter as Router, Route } from "react-router-dom"; import ArticleListing from "./ArticleListing"; import ArticleView from "./ArticleView"; class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <h1 className="App-title">Our blog</h1> </header> {/* Specifies routes and their components */} <Router> <div> <Route exact path="/" component={ArticleListing} /> <Route path="/post/:slug" component={ArticleView} /> </div> </Router> </div> ); } } export default App;
    import React, { Component } from "react"; import "./App.css"; // Imports routing dependencies from react-router-dom and our components import { BrowserRouter as Router, Route } from "react-router-dom"; import ArticleListing from "./ArticleListing"; import ArticleView from "./ArticleView"; class App extends Component { render() { return ( <div className="App"> <header className="App-header"> <h1 className="App-title">Our blog</h1> </header> {/* Specifies routes and their components */} <Router> <div> <Route exact path="/" component={ArticleListing} /> <Route path="/post/:slug" component={ArticleView} /> </div> </Router> </div> ); } } export default App;

    Notes:

    • The line <Route exact path="/" component={ArticleListing} /> specifies that if the browser URL matches / the ArticleListing component should be rendered in this place.
    • Paths can contain parameters, for example, path="/post/:slug", which are delivered to the target component through props.

    Overwrite the App.css file to:

    • CSS
    .App { padding: 0 20%; } .App-header { background-color: #222; height: 150px; padding: 20px; color: white; }
    .App { padding: 0 20%; } .App-header { background-color: #222; height: 150px; padding: 20px; color: white; }
    • Now, you can run npm start to start a development server.
    • Test your application by going to http://localhost:3000/ and http://localhost:3000/post/anything URLs. The app will auto-reload every time you save a file to reflect the latest changes, you can keep it running as you code.

    5. Listing articles

    In the src folder, modify the ArticleListing.js component to retrieve all published articles in the project and render them as links.

    • JavaScript
    import React, { Component } from "react"; import { Link } from "react-router-dom" import { DeliveryClient } from "@kentico/kontent-delivery"; const client = new DeliveryClient({ projectId: "a0a9d198-e604-007a-50c9-fecbb46046d1" }); class ArticleListing extends Component { constructor(props) { super(props); this.state = { loaded: false }; } fetchArticles() { client.items() .type("article") .elementsParameter(["url_pattern", "title"]) .getObservable() .subscribe(response => { console.log(response.items); this.setState({ loaded: true, articles: response.items }); }); } componentDidMount() { this.fetchArticles(); } render() { if (this.state.loaded) { return ( <ul> {this.state.articles.map((article) => { return ( <li key={article.url_pattern.value}> <Link to={`/post/${article.elements.url_pattern.value}`}> {article.title.text} </Link> </li> ) })} </ul> ); } else { return ( <div> Loading... </div> ) } } } export default ArticleListing;
    import React, { Component } from "react"; import { Link } from "react-router-dom" import { DeliveryClient } from "@kentico/kontent-delivery"; const client = new DeliveryClient({ projectId: "a0a9d198-e604-007a-50c9-fecbb46046d1" }); class ArticleListing extends Component { constructor(props) { super(props); this.state = { loaded: false }; } fetchArticles() { client.items() .type("article") .elementsParameter(["url_pattern", "title"]) .getObservable() .subscribe(response => { console.log(response.items); this.setState({ loaded: true, articles: response.items }); }); } componentDidMount() { this.fetchArticles(); } render() { if (this.state.loaded) { return ( <ul> {this.state.articles.map((article) => { return ( <li key={article.url_pattern.value}> <Link to={`/post/${article.elements.url_pattern.value}`}> {article.title.text} </Link> </li> ) })} </ul> ); } else { return ( <div> Loading... </div> ) } } } export default ArticleListing;

    Notes:

    • Instead of requesting entire articles you can use the elementsParameter to only retrieve the elements you need – the title of the article and its URL.
    • You can paste your own Sample Project ID into the DeliveryClient or keep using the a0a9d198-e604-007a-50c9-fecbb46046d1 project – it contains everything required for this tutorial. If you do not have access to the Sample Project, contact your subscription admin who can generate the Sample Project and invite you to it.

    Now, when you check your app in the browser, it will display a list of published articles:

    6. Viewing an article

    In the src folder, modify the ArticleView.js component to retrieve a single article and render it on the page.

    • JavaScript
    import React, { Component } from "react"; import { Link } from "react-router-dom" import { DeliveryClient } from "@kentico/kontent-delivery"; const client = new DeliveryClient({ projectId: "a0a9d198-e604-007a-50c9-fecbb46046d1" }); class ArticleView extends Component { constructor(props) { super(props); this.state = { loaded: false, article: null }; } fetchArticle(slug) { client.items() .equalsFilter("elements.url_pattern", slug) .depthParameter(1) .getObservable() .subscribe((response) => { console.log(response); this.setState({ loaded: true, article: response.items[0] }); }); } componentDidMount() { let slug = this.props.match.params.slug; this.fetchArticle(slug); } render = (props) => { if (this.state.loaded) { const article = this.state.article; const title = article.title.value; const bodyCopy = article.body_copy; return ( <div> <Link to="/">Home</Link> <h1>{title}</h1> <div className="article_body" dangerouslySetInnerHTML={{ __html: bodyCopy.getHtml() }} /> </div> ); } else { return ( <div> Loading... </div> ); } } } export default ArticleView;
    import React, { Component } from "react"; import { Link } from "react-router-dom" import { DeliveryClient } from "@kentico/kontent-delivery"; const client = new DeliveryClient({ projectId: "a0a9d198-e604-007a-50c9-fecbb46046d1" }); class ArticleView extends Component { constructor(props) { super(props); this.state = { loaded: false, article: null }; } fetchArticle(slug) { client.items() .equalsFilter("elements.url_pattern", slug) .depthParameter(1) .getObservable() .subscribe((response) => { console.log(response); this.setState({ loaded: true, article: response.items[0] }); }); } componentDidMount() { let slug = this.props.match.params.slug; this.fetchArticle(slug); } render = (props) => { if (this.state.loaded) { const article = this.state.article; const title = article.title.value; const bodyCopy = article.body_copy; return ( <div> <Link to="/">Home</Link> <h1>{title}</h1> <div className="article_body" dangerouslySetInnerHTML={{ __html: bodyCopy.getHtml() }} /> </div> ); } else { return ( <div> Loading... </div> ); } } } export default ArticleView;

    Now, when you check your app in the browser and click on an article, the app will display its content:

    7. Clean-up

    This part is optional but useful for getting to know the best practices around React and Kentico Kontent.

    Separate DeliveryClient configuration

    It's good practice to keep your DeliveryClient configuration in a separate file and import it to your components or services.

    Create a config.js file in the src folder:

    • JavaScript
    import { DeliveryClient } from "@kentico/kontent-delivery"; export const client = new DeliveryClient({ projectId: "a0a9d198-e604-007a-50c9-fecbb46046d1" });
    import { DeliveryClient } from "@kentico/kontent-delivery"; export const client = new DeliveryClient({ projectId: "a0a9d198-e604-007a-50c9-fecbb46046d1" });

    Modify the ArticleView.js and ArticleListing.js files to use the client from config.js instead of the hard-coded one.

    • JavaScript
    // ADD import { client } from "./config"; // DELETE // import { DeliveryClient } from "kentico-cloud-delivery"; // const client = new DeliveryClient({ projectId: "a0a9d198-e604-007a-50c9-fecbb46046d1" });
    // ADD import { client } from "./config"; // DELETE // import { DeliveryClient } from "kentico-cloud-delivery"; // const client = new DeliveryClient({ projectId: "a0a9d198-e604-007a-50c9-fecbb46046d1" });

    Unsubscribe from observables

    The DeliveryClient object returns content from Kentico Kontent as a RxJS Observable object you can subscribe to. Your components should unsubscribe from observables before being destroyed.

    The recommended approach is to use the takeUntil method to manage your component's subscriptions:

    • JavaScript
    import { client } from "./config"; import { Subject } from "rxjs"; import { takeUntil } from "rxjs/operators"; // ... let unsubscribeSubject = new Subject(); class ArticleListing extends Component { // ... fetchArticles() { client.items() .type("article") .elementsParameter(["url_pattern", "title"]) .toObservable() // unsubscribe when unsubscribeSubject fires .pipe(takeUntil(unsubscribeSubject)) .subscribe(response => { console.log(response.items); this.setState({ loaded: true, articles: response.items }); }); } unsubscribe() { unsubscribeSubject.next(); unsubscribeSubject.complete(); } componentWillUnmount() { this.unsubscribe(); } }
    import { client } from "./config"; import { Subject } from "rxjs"; import { takeUntil } from "rxjs/operators"; // ... let unsubscribeSubject = new Subject(); class ArticleListing extends Component { // ... fetchArticles() { client.items() .type("article") .elementsParameter(["url_pattern", "title"]) .toObservable() // unsubscribe when unsubscribeSubject fires .pipe(takeUntil(unsubscribeSubject)) .subscribe(response => { console.log(response.items); this.setState({ loaded: true, articles: response.items }); }); } unsubscribe() { unsubscribeSubject.next(); unsubscribeSubject.complete(); } componentWillUnmount() { this.unsubscribe(); } }

    Use the same approach for ArticleView.js.

    You can find a more detailed explanation online:

    Build and deploy

    Because your application doesn't require any server-side code, you can host it as a collection of static files for free on Surge, Github pages, or a similar service.

    Run the following commands to build and deploy your app using Surge.

    • shell
    npm install --global surge # Build your app npm run build # Rename index.html to 200.html in the build folder mv build/index.html build/200.html # Run surge to upload your built app surge build
    npm install --global surge # Build your app npm run build # Rename index.html to 200.html in the build folder mv build/index.html build/200.html # Run surge to upload your built app surge build

    Notes:

    • Before deploying, you need to rename the built index.html file to 200.html to enable client-side routing.
    • The first time you run Surge, it will prompt you to set up your account. Every other time it will confirm the build directory and generate a funny subdomain for your app.
    • You can store the set of commands for deploying your app as a script inside your package.json file.
    • JSON
    { // ... "scripts": { "deploy": "npm run build && mv build/index.html build/200.html && surge build <your domain>.surge.sh" // ... }
    { // ... "scripts": { "deploy": "npm run build && mv build/index.html build/200.html && surge build <your domain>.surge.sh" // ... }

    What's next?

    Don't stop now! Continue with part 2

    Now that you've created a simple React app that retrieves and displays articles from a Kentico Kontent Sample Project, you can continue with Building your first React app, part 2.