{"id":13314677,"url":"https://github.com/kontent-ai-bot/sample-app-graphql-react","last_synced_at":"2025-12-30T18:36:37.450Z","repository":{"id":44619276,"uuid":"364264394","full_name":"kontent-ai/sample-app-graphql-react","owner":"kontent-ai","description":"Sample React.js app demonstrating Kontent GraphQL capabilities.","archived":false,"fork":false,"pushed_at":"2023-08-08T07:11:12.000Z","size":122599,"stargazers_count":4,"open_issues_count":8,"forks_count":0,"subscribers_count":17,"default_branch":"main","last_synced_at":"2025-04-07T13:11:14.719Z","etag":null,"topics":["graphql","headless","headless-cms","kentico-kontent","kontent-ai","kontent-ai-sample","react","sample","website"],"latest_commit_sha":null,"homepage":"https://kontent-ai.github.io/sample-app-graphql-react/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kontent-ai.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null}},"created_at":"2021-05-04T13:25:34.000Z","updated_at":"2023-06-29T04:57:11.000Z","dependencies_parsed_at":"2023-01-20T08:32:21.831Z","dependency_job_id":null,"html_url":"https://github.com/kontent-ai/sample-app-graphql-react","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kontent-ai%2Fsample-app-graphql-react","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kontent-ai%2Fsample-app-graphql-react/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kontent-ai%2Fsample-app-graphql-react/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kontent-ai%2Fsample-app-graphql-react/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kontent-ai","download_url":"https://codeload.github.com/kontent-ai/sample-app-graphql-react/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252156938,"owners_count":21703378,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["graphql","headless","headless-cms","kentico-kontent","kontent-ai","kontent-ai-sample","react","sample","website"],"created_at":"2024-07-29T18:11:53.355Z","updated_at":"2025-12-30T18:36:37.444Z","avatar_url":"https://github.com/kontent-ai.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003e [!Warning]\n\u003e Deprecation Notice: This repository is no longer maintained.\n\u003e\n\u003e We encourage you to explore our latest solution [Kickstart React GraphQL App](https://github.com/kontent-ai/kickstart-react-graphql-app)\n\n# Kontent.ai Sample app GraphQL React\n\n[![Node.js CI](https://github.com/kontent-ai/sample-app-graphql-react/actions/workflows/node.js.yml/badge.svg)](https://github.com/kontent-ai/sample-app-graphql-react/actions/workflows/node.js.yml)\n[![Live Demo](https://img.shields.io/badge/Live-DEMO-brightgreen.svg?logo=github\u0026logoColor=white)](https://kontent-ai.github.io/sample-app-graphql-react/)\n\n[![Stack Overflow](https://img.shields.io/badge/Stack%20Overflow-ASK%20NOW-FE7A16.svg?logo=stackoverflow\u0026logoColor=white)](https://stackoverflow.com/tags/kontent-ai)\n[![Konten.ai Discord](https://img.shields.io/discord/821885171984891914?color=%237289DA\u0026label=Kontent.ai%20Discord\u0026logo=discord)](https://discord.gg/SKCxwPtevJ)\n\nThis sample app showcase Kontent.ai GraphQL API endpoint usage in combination with React.js using Apollo client.\n\n## Getting started\n\nIn this section you can find how to get the application ready in development mode.\n\n### Prerequisites\n\n- [Node.js](https://nodejs.org/en/download/) (LTS recommended)\n\n### Run site in development\n\nIn the project directory, install all dependencies and run the development environment.\n\n```sh\nnpm install\nnpm start\n```\n\n🎉 Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\n#### (Optional) Create your own data source project in Kontent.ai\n\nThis optional section allows you to create your own copy of the project in Kontent.ai so that you can make changes. If you skip this step, the application is connected to the published data of a shared project that is read-only via API for you.\n\n##### Create Kontent.ai project\n\n1. Create an account on Kontent.ai\n   - [Create an account on Kontent.ai](https://app.kontent.ai/sign-up?utm_source=nextjs_boilerplate_example\u0026utm_medium=devrel).\n2. After signing up, [create an empty project](https://kontent.ai/learn/tutorials/manage-kontent/projects/manage-projects#a-create-projects).\n3. Go to the \"Project Settings\", select API keys and copy the following keys for further reference\n   - Project ID\n   - Management API key\n4. Use the [Kontent.ai Backup Manager](https://github.com/kontent-ai/backup-manager-js) and import data to the newly created project from [`kontent-backup.zip`](./kontent-backup.zip) file via command line:\n\n    ```sh\n     npm i -g @kentico/kontent-backup-manager@3.0.1\n     # or\n     yarn global add @kentico/kontent-backup-manager@3.0.1\n   \n     kbm --action=restore --projectId=\u003cProject ID\u003e --apiKey=\u003cManagement API key\u003e --zipFilename=kontent-backup\n    ```\n\n5. Go to your Kontent.ai project and [publish the imported items](https://kontent.ai/learn/tutorials/write-and-collaborate/publish-your-work/publish-content-items).\n\n##### Environment variables\n\n1. Set up environment variables\n\n   - Copy the `.env.template` file in this directory to `.env` (which will be ignored by Git):\n\n     ```sh\n     cp .env.template .env\n     ```\n\n1. Run the development server\n\n   ```sh\n   npm run start\n   ```\n\n🎉 Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\n\u003e By default, the content is loaded from a shared Kontent.ai project. If you want to use your own clone of the project so that you can customize it and experiment with Kontent, continue to the next section.\n\n|              Variable              | Required | Description                                                                              |\n| :--------------------------------: | :------: | :--------------------------------------------------------------------------------------- |\n|    REACT_APP_KONTENT_PROJECT_ID    |    NO    | Project identification                                                                   |\n| REACT_APP_KONTENT_GRAPHQL_ENDPOINT |    NO    | Kontent.ai GraphQL endpoint                                                                 |\n| REACT_APP_KONTENT_PREVIEW_API_KEY  |    NO    | Preview API key to retrieve unpublished content. If set, the application is fetching unpublished content, if not published content is being fetched.                                      |\n|    REACT_APP_GA_ANALYTICS_TOKEN    |    NO    | If you want to inject [Google analytics](https://developers.google.com/analytics) script |\n\n## Content editing development\n\nRun the development server:\n\n```sh\nnpm start\n```\n\n🎉 Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying content in Kontent.ai project. The page auto-updates as you edit the content, but you need to [publish the changes](https://kontent.ai/learn/tutorials/write-and-collaborate/publish-your-work/publish-content-items) in order to see them on site.\n\n### Available Scripts\n\n- `npm start` - Runs the app in the development mode. Open [http://localhost:3000](http://localhost:3000) to view it in the browser.\n- `npm test` - Launches the test runner in the interactive watch mode. See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.\n- `npm build` - Builds the app for production to the `build` folder. It correctly bundles React in production mode and optimizes the build for the best performance. See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.\n- `npm eject` - **Note: this is a one-way operation. Once you `eject`, you can’t go back!** If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.\n\n## About\n\nThis section describes the content model of the site and the use cases that are supposed to demonstrate GraphQL capabilities.\n\n### Content Model\n\nThe site is using a simple layout. A header with a Logo and a menu.\nDepending on the type of the \"Content\" you can have one of the layouts:\n\n- **Landing page** - List of various sections displayed as rows.\n- **Listing page** - A grid of homogenous content pieces - specifically articles in the sample.\n- **Simple page** - A simple page rendering the title, subtitle, image, and rich text element.\n\n![Page types](./docs/Next.js%20site%20layout.png)\n\n## Layout - global/shared data\n\nThis data is stored in `Homepage` content item containing information about SEO, sitemap, layout (menu, title, copyright), and logo.\n\nThis data loaded in the [App component](./src/App.js) as part of the query. Simplified version of the query could be seen below:\n\n```graphql\n{\n  homepage(codename: \"home_page\") {\n    title\n    favicon {\n      url\n    }\n    font {\n      _system_ {\n        codename\n      }\n    }\n    palette {\n      _system_ {\n        codename\n      }\n    }\n  }\n}\n```\n\n### Sitemap construction\n\nElement `subpages` of `Homepage`, prepared for [Web Spotlight feature](https://webspotlight.kontent.ai), is used as a root of the **Sitemap structure**. It is a hierarchical structure, that allows mapping URLs items in the sitemap to content items and vice-versa.\n\nTo achieve mapping \"URL\"\u003c-\u003e\"Navigation Item\", there is a content type named `Navigation Item` that could be linked hierarchically (since it is also containing `subpages` element). **This data (`mappings` property) is then used across tha application for link resolution.**\n\n![Sitemap level](docs/Sitemap-top-level-model.png)\n\nHierarchical Sitemap is showcased under `/about-us/more-information`.\n\n![Sitemap hierarchy](./docs/Sitemap-hierarchy-example.png)\n\n\u003e There are also `Listing pages` showcased on `/blog` listing page containing detail pages `/blog/\u003cPOST-URL-SLUG\u003e` i.e. `/blog/5-tips-to-solve-your-problems`. This registration is based in `Listing page` (described in [Content model section](#content-model)). Listing page specifies by the `content_type` text field what content items should be registered under its route (expecting this type has URL slug set) - more information in [Listing page section](#listing-page).\n\n![Listing page items registration](./docs/listing-page-example.png)\n\nSimplified version of the data loading:\n\n```graphql\n{\n  homepage(codename: \"home_page\") {\n    subpages {\n      items {\n        ... on NavigationItem {\n          ...SubpageNavigationItemFields # see ~/src/graphQLFragments.js\n          subpages {\n            items {\n              ...SubpageNavigationItemFields # see ~/src/graphQLFragments.js\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n### Menu\n\nThe menu is modeled using `main_menu` linked items element of the `Homepage` that contains the item with single-level list of menu items linked in `actions` element.\n\n![Menu Content model](./docs/menu-model.png)\n\nThe data loaded in the [App component](./src/App.js) are then rendered in the [Header component](./src/components/Header.js).\n\n![Menu Screenshot](./docs/menu-UI.png)\n\nSimplified version of the data loading:\n\n```graphql\n{\n  homepage(codename: \"home_page\") {\n    mainMenu(limit: 1) {\n      items {\n        ... on Menu {\n          _system_ {\n            codename\n          }\n          actions {\n            items {\n              ... on Action {\n                ...ActionFields # see ~/src/graphQLFragments.js\n              }\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n## Simple page\n\nRich text resolution itself depends on technology you are using. But Rich text element itself is providing all data for the resolution.\n\nFollowing sections showcase what properties you could use to load this data. All of these resolutions is showcases in `/style-guide` simple page.\n\n![Rich text resolution](docs/rich-tex-resolution.png)\n\n### Components\n\n```graphql\n{\n  simplePage(codename: \"style_guide\") {\n    content {\n      # Rich text element\n      html\n      components {\n        items {\n          _system_ {\n            id\n            codename\n            type {\n              _system_ {\n                codename\n              }\n            }\n          }\n          ... on Quote {\n            quoteText\n          }\n          ... on CodeBlock {\n            code {\n              html\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n### Inline linked items\n\n```graphql\n{\n  simplePage(codename: \"style_guide\") {\n    content {\n      # Rich text element\n      html\n      modularContent {\n        items {\n          _system_ {\n            id\n            codename\n            type {\n              _system_ {\n                codename\n              }\n            }\n          }\n          ... on Quote {\n            quoteText\n          }\n          ... on CodeBlock {\n            code {\n              html\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n### Links\n\n```graphql\n{\n  simplePage(codename: \"style_guide\") {\n    content {\n      # Rich text element\n      html\n      links {\n        items {\n          ... on Quote {\n            quoteText\n          }\n          ... on CodeBlock {\n            code {\n              html\n            }\n          }\n        }\n      }\n    }\n  }\n}\n```\n\n### Assets\n\n```graphql\n{\n  simplePage(codename: \"style_guide\") {\n    content {\n      # Rich text element\n      html\n      assets {\n        url\n        name\n        description\n        imageId\n      }\n    }\n  }\n}\n```\n\n## Landing page\n\nLanding page itself showcases a resolution of different content types linked in single linked items element.\n\n![Landing page section schema](./docs/sections-page.png)\n\n### Sections of the landing page linked items resolution\n\nLanding page is using linked items element called `sections`.\n\nIn this use case it is a list of section and application is using `system.type` to distinguish what component from `~/src/component/sections` to render. The logic is in `~src/LandingPage.js`.\n\n```graphql\n{\n  landingPage(codename: \"main_content\") {\n    sections {\n      items {\n        _system_ {\n          codename\n          type {\n            _system_ {\n              codename\n            }\n          }\n        }\n        ... on HeroSection {\n          image {\n            ...AssetFields # see ~/src/graphQLFragments.js\n          }\n          title\n          content {\n            ...RichTextFields # see ~/src/graphQLFragments.js\n          }\n          actions {\n            items {\n              ...ActionFields # see ~/src/graphQLFragments.js\n            }\n          }\n        }\n        ... on FeaturesSection {\n          title\n          subtitle {\n            ...RichTextFields # see ~/src/graphQLFragments.js\n          }\n          features {\n            items {\n              ... on Feature {\n                image {\n                  ...AssetFields # see ~/src/graphQLFragments.js\n                }\n                title\n                content {\n                  ...RichTextFields # see ~/src/graphQLFragments.js\n                }\n                actions {\n                  items {\n                    ...ActionFields # see ~/src/graphQLFragments.js\n                  }\n                }\n              }\n            }\n          }\n        }\n        ... on CtaSection {\n          title\n          subtitle {\n            ...RichTextFields # see ~/src/graphQLFragments.js\n          }\n          action {\n            items {\n              ...ActionFields # see ~/src/graphQLFragments.js\n            }\n          }\n        }\n        ... on ListingSection {\n          title\n          subtitle {\n            ...RichTextFields # see ~/src/graphQLFragments.js\n          }\n          orderBy\n          contentType\n          numberOfItems\n        }\n      }\n    }\n  }\n}\n```\n\n## Listing page\n\nListing page showcase a various features you can stumble upon if you want to list content items with possibility to provide a detail.\n\n![Listing page example](docs/Listing-page-UI.png)\n\n### Listing - detail\n\nThe core it so to implement the list with detail pages.\n\nWhat to list is defined in the `Listing page` content type by the `content_type` test element specifying, what types you want to list. It is possible to extend the use case, to define multiple types and limit/filter them somehow.\n\n![Blog Listing page example](docs/listing-page-example.png)\n\nThe implementation of the listing page is stored in `src/ListingPage.js`.\n\n```graphql\n{\n  post_All {\n    # Strongly typed collections of items based on `Post`content type\n    items {\n      _system_ {\n        type {\n          _system_ {\n            codename\n          }\n        }\n        codename\n      }\n      image {\n        ...AssetFields\n      }\n      title\n      slug\n      excerpt\n      publishingDate\n      author {\n        ... on Author {\n          firstName\n          lastName\n        }\n      }\n    }\n  }\n}\n```\n\nDetail page of the post is implemented in `~src/Post.js`:\n\n```graphql\nquery PostPageQuery($codename: String!) {\n  post(codename: $codename) {\n    seo {\n      ...SeoFields # see ~/src/graphQLFragments.js\n    }\n    _system_ {\n      type {\n        _system_ {\n          codename\n        }\n      }\n    }\n    image {\n      ...AssetFields # see ~/src/graphQLFragments.js\n    }\n    title\n    publishingDate\n    author(limit: 1) {\n      items {\n        ... on Author {\n          firstName\n          lastName\n        }\n      }\n    }\n    subtitle\n    content {\n      ...RichTextFields # see ~/src/graphQLFragments.js\n    }\n  }\n}\n```\n\n### Paging\n\nPaging information is provided by query string parameter `page`, so if you want a second page the URL would be `/blog?page=2`. The size of the page is hardcoded to `3`, but could be configurable as well as the pace number.\n\nIn GraphQl, you just use a filter in the query:\n\n```graphql\nquery PostsQuery($limit: Int, $offset: Int) {\n  post_All(limit: $limit, offset: $offset) {\n    # Strongly typed collections of items based on `Post`content type\n    items {\n      # ...\n    }\n  }\n}\n\n```\n\n### Filtering\n\nYou can extend the posts query setting `where` parameter in query.\n\n#### Filter blogs by author\n\n```graphql\nquery PostsQuery($author: String) {\n  post_All(where: {author: {containsAny: [$author]}}) {\n    # Strongly typed collections of items based on `Post`content type\n    items {\n      # ...\n    }\n  }\n}\n```\n\n#### Filter blog by persona\n\n```graphql\nquery PostsQuery($persona: String) {\n  post_All(where: {persona: {containsAny: [$persona]}}) {\n    # Strongly typed collections of items based on `Post`content type\n    items {\n      # ...\n    }\n  }\n}\n```\n\n#### Filter blog by multiple conditions (persona and author)\n\n```graphql\nquery PostsQuery($persona: String) {\n  post_All(where: { \n    AND: [ # It is also possible to use OR and create more complex filter queries\n          { persona: {containsAny: [$persona]} },\n          { author: {containsAny: [$author]} }\n    ]\n  }) {\n    # Strongly typed collections of items based on `Post`content type\n    items {\n      # ...\n    }\n  }\n}\n```\n\n---\n\n## Tracking\n\nThe package is including tracking header to the requests to Kontent, which helps to identify the adoption of the source plugin and helps to analyze what happened in case of error. If you think that tracking should be optional feel free to raise the feature or pull request.\n\n## Learn More\n\n\u003e This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).\n\nYou can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).\n\nTo learn React, check out the [React documentation](https://reactjs.org/).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkontent-ai-bot%2Fsample-app-graphql-react","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkontent-ai-bot%2Fsample-app-graphql-react","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkontent-ai-bot%2Fsample-app-graphql-react/lists"}