Building your first React app, part 1
In this tutorial, you'll use the Create React appOpens in a new window 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 generateOpens in a new window.
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 hereOpens in a new window, see the source code on GithubOpens in a new window, or explore a live exampleOpens in a new window 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:
- Kentico Kontent accountOpens in a new window with a Sample Project
- Node and npmOpens in a new window (npm@5.2.0 or later)
- An IDE (such as Visual Studio CodeOpens in a new window) or the text editor of your choice
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.
npx create-react-app my-kontent-react-appnpx create-react-app my-kontent-react-app
2. Install dependencies
Navigate to the project folder and use npm
to install all the required libraries:
cd my-kontent-react-app npm install --save react-router-dom rxjs @kentico/kontent-deliverycd 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 applicationrxjs
– 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:
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:
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:
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/
theArticleListing
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:
.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/
andhttp://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.
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']) .toObservable() .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.url_pattern.value}`}> {article.title.value} </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']) .toObservable() .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.url_pattern.value}`}> {article.title.value} </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 thea0a9d198-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 ProjectOpens in a new window 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.
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) .toObservable() .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.resolveHtml() }} /> </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) .toObservable() .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.resolveHtml() }} /> </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:
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.
// 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:
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.
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 buildnpm 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 to200.html
to enable client-side routingOpens in a new window. - 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.
{ // ... "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.