# Prepr Documentation - Full Export > Complete Prepr CMS documentation in one file. --- # Start building with Prepr This is the place to learn all about Prepr. Explore the topics below to start learning or create your [free account here](https://signup.prepr.io) if you haven't done so already. If you need any help, don't hesitate to reach out: \ [Join our Slack](https://slack.prepr.io) or [Reach out to our support team](https://prepr.io/support). - [Quick start guide](https://docs.prepr.io/quick-start-guide): Get started in minutes to get a feel for Prepr's capabilities. A short introduction of all key features. - [AI-friendly docs](https://docs.prepr.io/ai-friendly-docs): Get faster, more accurate code suggestions using the Prepr Docs LLMs. - [GraphQL API reference](https://docs.prepr.io/graphql-api): Discover the power of the GraphQL API; our read-only API. Learn about schemas and build advanced queries. ## Connect a front-end framework - [Next.js](https://docs.prepr.io/connecting-a-front-end-framework/nextjs) - [Astro](https://docs.prepr.io/connecting-a-front-end-framework/astro) - [Svelte](https://docs.prepr.io/connecting-a-front-end-framework/svelte) - [Laravel](https://docs.prepr.io/connecting-a-front-end-framework/laravel) - [Nuxt](https://docs.prepr.io/connecting-a-front-end-framework/nuxtjs) - [React](https://docs.prepr.io/connecting-a-front-end-framework/react) - [Vue.js](https://docs.prepr.io/connecting-a-front-end-framework/vuejs) - [Angular](https://docs.prepr.io/connecting-a-front-end-framework/angular) - [Node.js](https://docs.prepr.io/connecting-a-front-end-framework/nodejs) - [PHP](https://docs.prepr.io/connecting-a-front-end-framework/php) ## More resources - [Content modeling](https://docs.prepr.io/content-modeling): Learn about schema design concepts and how to create your schema in Prepr using common examples. - [Mutation API reference](https://docs.prepr.io/mutation-api): Our REST API is great for content mutation operations. - [Content management](https://docs.prepr.io/content-management): Learn about modeling data and working with copy, video, images and multi-lingual content. - [Setting up personalization](https://docs.prepr.io/personalization/setting-up-personalization): Set up personalization in your front end to provide visitors with an experience tailored to their needs and interests. - [Project setup](https://docs.prepr.io/project-setup): Experience how easy it is to develop with Prepr. Learn about CI/CD, data migration and webhooks. - [Integrations](https://docs.prepr.io/integrations): Integrations can extend Prepr to provide pieces of content or capabilities to the Prepr UI and APIs. Source: https://docs.prepr.io/index --- # Quick start guide *Estimated duration: 15-30 minutes* ## Introduction Prepr allows you to develop a dynamic, data-driven, web application with ease. This tutorial explains how to get started with Prepr. We will cover all the basics: modeling content types, adding content and images and retrieving content using the API. Follow the step-by-step guide below. ## Use case This tutorial explains how to manage articles and author profiles in Prepr and retrieve that content using the GraphQL API. For example, to create a blog. You can apply these principles to all kinds of situations. ![quick-start-guide-use-case](https://assets-site.prepr.io/w_1920/4e2edea6-dbeb-4ae8-bd2c-f78ebf6ace7e.jpg) ## Step 1: Create an environment If you haven't already done so, go to https://signup.prepr.io/ and sign up for a Prepr account. 1. After you sign up, you will be prompted to add an environment. Enter a **Name** and choose the **Default locale** and **Development stage**. ![add environment](https://assets-site.prepr.io/ki6nbcjodkg//updated-navigation-add-new-environment.png) 2. On the next screen, you will be prompted to either load demo data or to start from scratch. While importing demo data can help you explore Prepr features with preset schema and content items, for the purpose of this tutorial we recommend starting from scratch to experience the whole setup process yourself. To get a clean environment, click **Start from scratch**. ![Start from scratch](https://assets-site.prepr.io/28i2x8s5e9ld//new-environment-start-from-scratch.png) That’s it. You’ve created your first environment in Prepr. In the next step, you can add models and fields for our blog use case. ## Step 2: Create content models A headless CMS allows you to define a structure for your content according to your needs. We call that content modeling. For this example, you need an **Article** model and a **Person** model. ![quick-start-guide-create-content-model](https://assets-site.prepr.io/w_1920/5c7b4cee-65ee-493b-8df8-58477e39e81f.jpg) Let’s create these models in Prepr: 1. Click the **Schema** tab to open the *Schema Editor*. 2. In the *Models* section, click the **+ Add model** link. 3. Choose **Multi-item model** and enter *Person* in the **Display name** field and click **Next** and **Save**. Check out the [Models doc](https://docs.prepr.io/content-modeling/managing-models#manage-settings) for more details on additional options. ![empty person model](https://assets-site.prepr.io/1eetuzhu9jen//quick-start-guide-empty-person-model.png) 5. Drag and drop the **Text** field type, from the list on the right into your model.\ a. Enter *Name* in the **Display name** field and click **Save**. 6. Drag and drop another **Text** field type, from the list on the right into your model.\ a. Enter *Bio* in the **Display name** field.\ b. Click the **Settings** tab.\ c. Select *Text area* and click **Save**. 7. Drag and drop the **Assets** field type, from the list on the right into your model.\ a. Choose the **Multi-asset field** option.\ b. Enter *Image* in the **Display name** and click **Save**. Your Person model should now look like this: ![quick-start-guide-person-model](https://assets-site.prepr.io/5ulschu2ycou//quick-start-guide-person-model-with-fields.png) Now you can add the Article model. 1. Click the **+ Add model** button on the left. 2. Choose the **Multi-item model** option, enter *Article* in the **Display name** field, click **Next** and **Save**. Check out the [Models doc](https://docs.prepr.io/content-modeling/managing-models#manage-settings) for more details on additional options. Your model should look something like this: ![Empty article model](https://assets-site.prepr.io/4stb6440w4d9//quick-start-guide-empty-article-model.png) And add the fields: 4. Drag and drop the **Text** field type, from the list on the right into your model.\ a. Enter *Headline* in the **Display name** field and click **Save**. 5. Drag and drop another **Slug** field type, from the list on the right into your model.\ a. Click **headline** in the box below to fill the Slug template with `{headline}`.\ b. Click **Save**. Now we’re going to add the author field. This is not a text field, but a reference to the Person model you created earlier. A reference field allows you to link to other content items, as in this case to a Person. 6. Drag and drop the **Content reference** field type, from the list on the right into your model.\ a. Enter *Author* in the **Display name** field.\ b. Select the *Person* model and click **Save**. ![ Article model with author ](https://assets-site.prepr.io/55ylhz2jqa2m//quick-start-guide-filled-article-model.png) 7. Next, add the rest of the fields below in the same way as described above: - *Intro* - **Text** field - *Image* - **Assets** field - *Content* - **Dynamic content** field - *Tags* - **Tags** field See an overview of [all field types](https://docs.prepr.io/content-modeling/field-types). Your model should look like the image below. ![Article model completed](https://assets-site.prepr.io/332yhkppxy2u//quick-start-guide-article-with-all-fields.png) ## Step 3: Create content items Now that you've created the models, you can add content items. Start by adding a *Person*: 1. Go to the **Content** tab, click the **Add item** button and choose **Person**. 2. Fill out the **Name** and **Bio** fields. 3. Drag and drop an image into the **Image** field or click it to add an image from your local storage. ![create a person](https://assets-site.prepr.io/vfavn56rrwb//example-person.png) 6. Click the **Publish** dropdown and select the **Publish and close** option. Now, add an article: 1. Click the **Add item** button again. 2. This time, select **Article**. 3. Enter a **Headline**. You will notice the slug field automatically being populated. Copy this slug value to retrieve your article in [Step 5](#step-5-retrieve-a-single-article-from-the-api). 4. Click the **+ Person** link to choose and add a person to the *Author* field. ![create article](https://assets-site.prepr.io/38x1tn6pm1x1//example-article.png) Now fill in the remaining fields. 6. Add some text and images to the *Content* field. 7. Add some tags in the *Tags* field. Add tags by selecting an existing tag in the list, or adding a new tag and clicking **Enter**. 8. Click the **Publish** dropdown and select the **Publish and close** option. The next step is to retrieve the content items from the API so you can display the content in your web application. ## Step 4: Retrieve articles from the API The easiest way to experience content retrieval through the API is to use the API explorer: 1. Click the icon and choose the **Access tokens** option. 2. Click to open the *GraphQL Production* token details. 3. Open the API explorer by clicking the **Open API Explorer** link. ![GraphQL production access token example](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) Make sure you see a green dot and your access token at the end of the url. If you cannot connect, please contact support. ![API explorer example](https://assets-site.prepr.io/350zhceg8aek//api-explorer-example.png) With the explorer, you can easily create and test API queries. Let’s create a query to retrieve a list of all articles: 4. Add the following query ```graphql copy query { Articles { items { _id headline } } } ``` Note that you can retrieve the ID for all content items using the system field *\_id*. You can recognize system fields by the underscore in front of the field name. Check out [all system fields](https://docs.prepr.io/graphql-api/schema-system-fields) for more information. 5. Click **Run** to execute the query The result should look something like this: ```json copy { "data": { "Articles": { "items": [ { "_id": "4fcad70d-2beb-4886-bd8d-1753020bf315", "headline": "How to get the best deals : Insider tips and tricks" } ] } } } ``` As you can see, the response includes a list of all the items. At the moment, there is only one article, which is listed including its ID and headline. Let's expand the query so that you retrieve the other fields as well. ```graphql copy query { Articles( where: { _publish_on_gt : "2025-06-15T09:00:00+00:00" } ) { items { _id _slug _publish_on author { _id name image { url(width:800) } } headline image { url(width: 1000) } intro tags { body } } } } } ``` In the query above, we added a condition to get all articles published on or after June 15th, 2025. This query includes the following fields that we want to see in the result: - *\_slug* field - *\_publish\_on* field to retrieve the publication date - *author* field to retrieve reference fields ([learn more](https://docs.prepr.io/graphql-api/schema-field-types#content-reference)) - *image* field to retrieve assets ([learn more](https://docs.prepr.io/graphql-api/schema-field-types#assets)) - *content* field to retrieve a dynamic content field ([learn more](https://docs.prepr.io/graphql-api/schema-field-types-dynamic-content-field)) - *tags* field to retrieve tags ([learn more](https://docs.prepr.io/graphql-api/schema-field-types#tag)) Check out the documentation for [all field types](https://docs.prepr.io/graphql-api/schema-field-types) and how to retrieve [multiple content items](https://docs.prepr.io/graphql-api/fetching-collections) for more information. - Run the query. The response should look something like this: ```json copy { "data": { "Articles": { "items": [ { "_id": "38a05b5c-98cf-4275-8d2b-3748f49cc104", "_slug": "how-to-get-the-best-deals-insider-tips-and-tricks", "_publish_on": "2025-10-07T12:08:00+00:00", "author": [ { "_id": "dee58849-bb0a-4ceb-a288-7adc9e623602", "name": "Emma Carter", "image": { "url": "https://ml98.stream.prepr.io/6iku17cg8lc8/w_800/emma-carter-profile.jpg" } } ], "headline": "How to get the best deals : Insider tips and tricks", "image": { "url": "https://ml98.stream.prepr.io/6i57ud05jnjq/w_1000/business-deal-photo.jpg" }, "intro": "Want to lease a car without overpaying? Follow these expert tips to negotiate the best lease deal, avoid common pitfalls, and maximize your savings.", "tags": [ { "body": "car deals" }, { "body": "tips and tricks" } ] } ] } } } ``` And voila, a JSON response containing all the data from your content items. We just queried a list of articles that you can use to show on a blog overview page. On that page, you give each item a unique URL based on the slug—for example: ``` https://yourdomain.com/articles/how-to-get-the-best-deals-insider-tips-and-tricks ``` ## Step 5: Retrieve a single article from the API You want to show the whole article when a visitor clicks the link. Retrieving a single content item can be done using the below query. Replace the slug value with the value that you copied in [Step 3](#step-3-create-content-items). ```graphql copy query { Article( slug: "how-to-get-the-best-deals-insider-tips-and-tricks") { _id headline } } ``` In the query above, we specify a condition to return an item with the matching slug. Update the slug value in quotes to match the slug of the article that you created. When you run this query you get the following result: ```json copy { "data": { "Article": { "_id": "38a05b5c-98cf-4275-8d2b-3748f49cc104", "headline": "How to get the best deals : Insider tips and tricks" } } } ``` The last thing you need to know is how to retrieve the article content. Expand the query like this: ```graphql copy query { Article(slug: "how-to-get-the-best-deals-insider-tips-and-tricks") { _id headline author { _id name image { url(width:800) } } intro image { url(width: 1000) } content { ... on Text { format body } ... on Assets { items { url(width:600) } } } tags { body } } } ``` You've already seen most of the fields when you retrieved the article list. The content field is new. The preceding query shows how to retrieve a dynamic content field. Per element type, you fetch the information. In this case, for text elements and assets only, however, there are many more element types. Check out the [dynamic content field documentation](https://docs.prepr.io/graphql-api/schema-field-types-dynamic-content-field) for all the details. Here’s the result: ```json copy { "data": { "Article": { "_id": "38a05b5c-98cf-4275-8d2b-3748f49cc104", "headline": "How to get the best deals : Insider tips and tricks", "author": [ { "_id": "dee58849-bb0a-4ceb-a288-7adc9e623602", "name": "Emma Carter", "image": { "url": "https://ml98.stream.prepr.io/6iku17cg8lc8/w_800/emma-carter-profile.jpg" } } ], "intro": "Want to lease a car without overpaying? Follow these expert tips to negotiate the best lease deal, avoid common pitfalls, and maximize your savings.", "image": { "url": "https://ml98.stream.prepr.io/6i57ud05jnjq/w_1000/business-deal-photo.jpg" }, "content": [ { "format": null, "body": "

Leasing a car can be a great way to drive a new vehicle without the high upfront cost of purchasing. However, not all lease deals are created equal. If you want to save money and get the most value, follow these expert tips to secure the best lease deal possible.

" }, { "format": null, "body": "

1. Understand the Key Lease Terms

" }, { "format": null, "body": "

Before negotiating, it’s essential to understand how leases work. Here are the critical terms you should know:

" }, { "format": null, "body": "

• Capitalized Cost (Cap Cost) – The vehicle’s price before lease calculations. Like when buying, this can be negotiated down.

" }, { "format": null, "body": "

• Residual Value – The car’s estimated value at the end of the lease. Higher residual values mean lower monthly payments.

" }, { "format": null, "body": "

• Money Factor – Equivalent to the interest rate on your lease. The lower, the better.

" }, { "format": null, "body": "

• Mileage Limit – Most leases have annual limits (e.g., 10,000–15,000 miles). Exceeding this incurs extra charges.

" }, { "format": null, "body": "

2. Negotiate the Capitalized Cost

" }, { "format": null, "body": "

Many people assume lease prices are fixed—they’re not! You can negotiate the cap cost just like when buying a car. Research the market price of the vehicle and ask for a discount. The lower the price, the lower your monthly lease payments.

" }, { "format": null, "body": "

3. Look for Manufacturer Lease Specials

" }, { "format": null, "body": "

Car manufacturers frequently offer special lease deals, reducing monthly payments, down payments, or interest rates. These can include:

" }, { "format": null, "body": "

• Cash rebates

" }, { "format": null, "body": "

• Loyalty discounts

" }, { "format": null, "body": "

• Zero down-payment leases

" }, { "format": null, "body": "

Check official websites and dealership promotions for these offers.

" }, { "format": null, "body": "

4. Choose a Car with a High Residual Value

" }, { "format": null, "body": "

Cars that retain their value well will have a higher residual value, leading to lower monthly payments. Brands like Toyota, Honda, and Lexus typically have strong resale values.

" }, { "format": null, "body": "

5. Understand Lease Fees and Extra Costs

" }, { "format": null, "body": "

Be aware of the following potential extra costs:

" }, { "format": null, "body": "

• Acquisition Fee – A fee for setting up the lease.

" }, { "format": null, "body": "

• Disposition Fee – Charged at the end of the lease if you return the car.

" }, { "format": null, "body": "

• Excess Mileage Charges – Avoid these by choosing a lease with the right mileage limit.

" }, { "format": null, "body": "

6. Consider a One-Pay Lease

" }, { "format": null, "body": "

Instead of making monthly payments, some dealerships offer a single lump-sum payment lease. If you can afford it, this may save you thousands in interest fees.

" }, { "format": null, "body": "

7. Final Tip: Read the Lease Agreement Carefully

" }, { "format": null, "body": "

Before signing, review every detail of the lease to avoid hidden costs. If anything is unclear, ask for clarification.

" } ], "tags": [ { "body": "car deals" }, { "body": "tips and tricks" } ] } } } ``` For more details check out the [fetching single content items](https://docs.prepr.io/graphql-api/fetching-single-items) API reference. Congratulations, you have completed your first steps with Prepr. From modeling and creating content items to retrieving content using the API. With that, you have mastered the basics of Prepr. ## Step 6: Connect your front end The next step is to connect your web application. We have guides for all major front-end frameworks to quickly get you up and running. - [Next.js](https://docs.prepr.io/connecting-a-front-end-framework/nextjs) - [Nuxt](https://docs.prepr.io/connecting-a-front-end-framework/nuxtjs) - [Laravel](https://docs.prepr.io/connecting-a-front-end-framework/laravel) - [Angular](https://docs.prepr.io/connecting-a-front-end-framework/angular) - [Node.js](https://docs.prepr.io/connecting-a-front-end-framework/nodejs) - [PHP](https://docs.prepr.io/connecting-a-front-end-framework/php) - [React](https://docs.prepr.io/connecting-a-front-end-framework/react) - [Vue.js](https://docs.prepr.io/connecting-a-front-end-framework/vuejs) ## Want to learn more? Of course, there is much more to explore. For example, with Prepr, you can perform A/B testing, present recommendations and personalize the visitor journey. For more advanced topics, please refer to the rest of the documentation, or contact one of our specialists. Check out the following chapters: - [A/B testing](https://docs.prepr.io/ab-testing) - [Personalization](https://docs.prepr.io/personalization) - [Recommendations](https://docs.prepr.io/recommendations) ## Schedule a free consultation Do you want to get started but still have questions or want a demo? [Schedule a free call](https://prepr.io/get-a-demo) with a Prepr solution engineer. During the consultation we can provide recommendations on topics like: - Content modeling for the desired use case - Working with multiple environments, websites and languages - Creating personalized experiences for your website visitors - A/B testing and other optimization strategies Source: https://docs.prepr.io/quick-start-guide --- # Changelog Beautiful new features and important updates are added to Prepr on a daily basis. This changelog gives you an insight into the most eye-catching releases. Be aware that updates can be rolled out in phases so they may not always be available in all Prepr environments at the same time. ## New grid view option for remote content *October 27th, 2026* With the new grid view option for remote content items, you can switch to a more visual layout to make it easy choosing items based on the thumbnail instead of a list of fields. ![Remote content grid view example](https://assets-site.prepr.io/owezjg4h6he//remote-content-grid-view-example.png) This means you find and pick the right products faster with less scrolling, and quicker content creation. Check out the [Remote content field settings](https://docs.prepr.io/content-modeling/field-types#remote-content-field) for more details. ## Choosing your own repo path for schema sync *October 22nd, 2026* You can now choose your own repository path when connecting GitHub, GitLab, Bitbucket, or Azure DevOps to sync your Prepr schema. This means you keep your Prepr schema alongside your code, organized your way. Especially for teams working with a monorepo since you can easily define separate schema locations for different sites. > **Note:** > > Existing connections continue using the `prepr/schema` folder structure. Check out the [syncing a schema guide](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema#connect-to-github) for more details. ## New GraphQL API version 2026-10-19 is available *October 21st, 2026* The GraphQL API has been updated with the following additions and changes: - There is now support for SuperOffice form embeds. - Improved `_environment_id` retrieval for nested content items. - *Dynamic content* fields now use a dedicated union type that contains the elements, components, and remote sources configured for that field. This replaces the shared `_prepr_types` union and gives you more accurate generated types. - *Dynamic content* fields now return a nullable list of non-null entries, for example `[Article_Body!]`. Check out the [GraphQL API upgrade guide](https://docs.prepr.io/graphql-api/upgrade-guide#version-2026-10-19) for more details and the steps to update your app. ## Manage assets and asset folders with the Prepr MCP server *October 15th, 2026* With the Prepr MCP server, you can use natural language prompts to manage your media library. The new actions include editing and deleting assets, and creating, renaming, and deleting asset folders. For example, when you need to reorganize your media library or clean up outdated assets. This means you keep your media library tidy and up to date quickly and with less effort. Check out the [MCP server release notes](https://docs.prepr.io/prepr-mcp-server/release-notes#manage-assets-and-asset-folders-with-ai) for more details. ## Improved asset detail page *October 14th, 2026* We’ve redesigned the asset detail page to match our streamlined content editing interface. ![example asset detail page](https://assets-site.prepr.io/2v1p7l890oi7//asset-detail-page-shortened.png) It now gives you a cleaner, and more intuitive layout, helping you review and edit individual assets faster. Check out the [Managing assets guide](https://docs.prepr.io/content-management/managing-assets/managing-assets) for more details. ## Required validation for assets fields on upload *October 14th, 2026* We’ve added required field validation directly to the asset upload process. You can now fill in missing text fields as you upload. ![Fill required Asset text field example](https://assets-site.prepr.io/3b2m0buvqrk//fill-required-fields.png) This keeps your asset data consistent from day one. So, no more tedious cleanup later. Check out the [Managing assets guide](https://docs.prepr.io/content-management/managing-assets/managing-assets#uploading-assets) for more details. ## On-demand AI text generation for assets *October 14th, 2026* Previously, AI generation of text fields for assets was only possible automatically during a new upload. You can now generate or regenerate asset text fields manually whenever needed. ![Generate image text with AI](https://downloads-site.prepr.io/4exdaeiyjneb-generate-image-text-with-ai.gif) This means you adhere to the four-eyes principle when using AI and have full control over the text values in your assets. Check out the [Auto-generate text for images guide](https://docs.prepr.io/ai-image-text) for more details. ## New List view and bulk editing of assets *October 14th, 2026* You can now choose to view a simple list of assets instead of the default grid view. Combined with the new bulk editing feature, you get a flexible display to easily select the multiple assets you need to edit at the same time. We've also renamed *Collections* to *Folders* to better reflect how you store and organize your assets. ![List view and edit option example](https://assets-site.prepr.io/384o8mofolno//list-view-and-edit-option.png) This means you can be more productive with a large media library. Check out the [Asset bulk actions](https://docs.prepr.io/content-management/managing-assets/managing-assets#edit) for more details. ## Introducing the Mimir DAM integration *October 14th, 2026* You can now connect Mimir to Prepr so content editors can use assets from this DAM platform directly in content items. ![Example content item - Mimir asset](https://assets-site.prepr.io/516ugzi0icsm//example-content-item-add-mimir-asset.png) This gives teams a single source for assets while keeping content production inside Prepr. Check out the [Mimir integration guide](https://docs.prepr.io/integrations/mimir) for more details. ## Managing MCP server permissions per environment *October 8th, 2026* You can now choose permissions per environment for all Prepr MCP server requests. For example, you can disable **Schema → Write** access in your production environment, so no AI agent can change the schema there even if the user has the role permission to do so. ![MCP server schema permissions](https://assets-site.prepr.io/6tpwod8c630n//mcp-server-permission-settings.png) This means you can safely let AI agents work in your environments without risking unintended changes to production. Check out the [MCP server authorization guide](https://docs.prepr.io/prepr-mcp-server/authorization#managing-mcp-server-permissions) for more details. ## Signed URL when uploading assets *October 7th, 2026* You can now request a temporary, secure AWS S3 upload URL by passing `direct_s3_upload=true` to the Asset Upload API. Your client can then use this signed URL to upload the file directly to S3, bypassing Prepr’s servers. This new option supports faster, more reliable uploads, especially for large files and bulk asset migrations or syncs. Check out the [mutation asset upload guide](https://docs.prepr.io/mutation-api/managing-assets#get-signed-url) for more details. ## Introducing schema management with the Prepr MCP server *October 6th, 2026* With the Prepr MCP server, you can now request AI agents to manage your Prepr schema with natural language prompts. For example, when you need to generate the schema from scratch based on a UX design. This means you can automate the grunt work to speed up your implementation. This release includes additional features and improvements, such as *Scheduling unpublished items* and *Clearer search results*. Check out the [MCP server release notes](https://docs.prepr.io/prepr-mcp-server/release-notes) for more details. ## Adding component sections *October 2nd, 2026* We've implemented the option to add sections to components. With component sections, you can now group related fields in components like you can in models. ![Add sections to component](https://assets-site.prepr.io/5q543k4qqqw8//add-section-to-component-example.png) This feature creates better visibility of component fields in content items and helps content editors to edit these cleaner content items more easily. ![Example content item with component sections](https://assets-site.prepr.io/zy4r82wk34e//content-item-example-with-component-sections.png) Check out the [Managing components guide](https://docs.prepr.io/content-modeling/managing-components#add-sections) for more details. ## Read-only access to the schema *September 28th, 2026* You can now give a user role read-only access to the schema. Users with this role can view models, components, enumerations and remote sources, but can't create, update, delete or sync them. ![Role with schema read access](https://assets-site.prepr.io/3mcuq6l40so7//schema-read-only-example.png) This means you protect your schema integrity while giving access to team members who need context but shouldn't edit. So, no more tickets just to check how a field works. Check out the [roles and permissions guide](https://docs.prepr.io/project-setup/managing-roles-and-permissions#schema-permission) for more details. ## Marking favorite models for faster content creation *September 24th, 2026* You can now mark your favorite models to show them at the top of the model selection list when creating content items. By marking the models you usually work with, you no longer need to search through a list of unrelated models. ![Favorite model example](https://assets-site.prepr.io/2e5xvn2xz51f//add-to-favorites-example.png) This means you and your editing team can create content items faster maintaining an efficient editing workflow. Check out the [content management guide](https://docs.prepr.io/content-management/managing-content/managing-content-items#create-a-content-item) for more details. ## Introducing content metrics *September 18th, 2026* With the new content item metrics you can easily see the impact of your content directly in the content item list. ![content metrics in the item list ](https://assets-site.prepr.io/urc3izslbj4//views-trend-in-content-item-list.png) When diving deeper, you can get more detailed metrics including the reach, engagement and performance for a specific content item. This means you get insights into what works to make improvements and focus on the content that works. Check out the [content metrics guide](https://docs.prepr.io/content-management/measuring-content-impact) for more details. ## Improved access token management *September 15th, 2026* We've just made the access token page clutter-free. Now, you’ll only see the information relevant to the specific type of token you're viewing. No more endless scrolling to see the details you need when managing access tokens. ![Example uncluttered access token](https://assets-site.prepr.io/l02d68togx8//new-graphql-api-access-token.png) Plus, when creating a token, you can now choose the exact type you need: *GraphQL API*, *REST API*, or *Customer API*. Check out the [REST API token example](https://docs.prepr.io/mutation-api/authorization) for more details. ## Copy and paste Stack field elements *September 2nd, 2026* You can now copy and paste components in *Stack* fields, including all values entered in the component. ![Copy and paste actions in stack](https://assets-site.prepr.io/4jrit25jpvcz//copy-and-paste-element.png) This makes it easier to quickly reuse existing components across different stack fields without having to configure them from scratch. The *Stack* field interface now also provides a more streamlined way to add models and components, with improved behavior for narrow views and a cleaner empty state. Check out the [stack field guide](https://docs.prepr.io/content-management/managing-content/creating-rich-content#working-with-a-stack-field) for more details. ## OneLogin integration now available for SSO *September 1st, 2026* You can now integrate OneLogin to Prepr to set up single sign-on (SSO) for your Prepr users using this cloud-based Identity and Access Management (IAM) solution. ![OneLogin SSO integration](https://assets-site.prepr.io/4ayguyd6znuw//onelogin-sso-integration-option.png) By activating this integration, you enhance security and give your OneLogin users an improved login experience. Check out the [SSO guide](https://docs.prepr.io/project-setup/setting-up-sso#onelogin) for more details. ## Prefilled AI prompts for segment and environment context *September 1st, 2026* We’ve added prefilled AI context prompts directly inside the *Segment* and *Environment* settings. You can now launch ChatGPT, Claude, or Gemini with one click to generate complete context profiles without writing prompts from scratch. ![example segment AI context](https://assets-site.prepr.io/7c2dud0rmric//example-segment-ai-context.png) Create relevant AI context in seconds for every audience. Check out the [segment settings](https://docs.prepr.io/personalization/managing-segments#defining-segment-context) and [environment settings](https://docs.prepr.io/project-setup/setting-up-environments#define-environment-context-for-ai-requests) for more details. ## Introducing the Bynder enterprise DAM integration *August 31st, 2026* You can now connect Bynder to Prepr so content editors can use assets from this enterprise DAM platform directly in content items. ![Adding Bynder asset to content](https://assets-site.prepr.io/6y05gevrwizs//adding-bynder-asset-to-content-item.png) This gives teams a single source for assets while keeping content production inside Prepr. Check out the [Bynder integration guide](https://docs.prepr.io/integrations/bynder) for more details. ## Introducing the framework-independent Prepr toolkit (Beta) *August 25th, 2026* Whether you’re building with Next.js, Astro, React, Nuxt.js, or Svelte, you can now automate front-end features like the preview, edit-mode and experiments with the new Prepr toolkit. This is a lightweight, high-performance toolkit based on JavaScript standards. If you’re currently using the legacy @prepr/nextjs package, we strongly recommend upgrading to the new Prepr toolkit to take advantage of its broader framework support and streamlined approach to integrating Prepr features. Follow the [migration guide](https://github.com/preprio/prepr-toolkit/blob/main/MIGRATION.md) to upgrade. Check out the [GitHub repo](https://github.com/preprio/prepr-toolkit) for more details and example projects. ## Faster content item loading for complex content structures *August 19th, 2026* We’ve significantly improved the performance of opening content items in the content editor, especially for content items with many language variants, components, or references. **What’s improved?** - Language variants now load on demand, instead of loading all locales upfront. - Stack field content is loaded more efficiently, reducing the amount of data processed when opening an item. **Impact** Customers with complex content structures may experience dramatically faster load times when opening content items. In some cases, we’ve measured up to **16x faster performance**. This improvement is particularly noticeable for: - Content items with many locales/translations. - Content items containing stack fields with numerous components and references. The result is a more responsive editing experience and reduced waiting time for content teams working with complex content models. Check out a [stack field example](https://docs.prepr.io/content-modeling/examples/page) for more details. ## Protected asset deletion *August 5th, 2026* To protect your content integrity, Prepr now prevents you from deleting assets that are currently in use by a content item. Previously, deleting an in-use asset only triggered a warning. ![blocked asset deletion](https://assets-site.prepr.io/1let42locjcu//asset-used-in-content-item.png) This change prevents broken websites, missing media, and downstream application errors caused by deleted assets. Check out the [asset management guide](https://docs.prepr.io/content-management/managing-assets/managing-assets#deleting-assets) for more details. ## Legacy API endpoint is now retired *August 1st, 2026* As previously announced, the legacy API endpoint `api.eu1.prepr.io` has been officially retired today and is no longer operational. Please confirm all requests have been migrated to the updated endpoints: - Write operations (mutations): `mutation.prepr.io` - Read-only operations (queries): `cdn.prepr.io` Other Prepr domains (such as `customers.prepr.io`) remain unaffected. This change only impacts the domain being called and does not alter your data or underlying application logic. If you experience any unexpected API errors or need assistance finalizing your migration, please reach out to [Prepr Support](mailto:support@prepr.io) immediately. ## Introducing the Leadinfo integration *July 23rd, 2026* With the new integration to this B2B lead generation software, you can segment website visitors based on their industry and company size. ![Leadinfo segment example](https://assets-site.prepr.io/3b9qmg1t70ml//leadinfo-example-segment.png) This allows you to personalize content for an enhanced user experience for your B2B audience. Check out the [Leadinfo integration guide](https://docs.prepr.io/integrations/leadinfo) for more details. ## MCP server: Expanded media support and schema settings *July 21st, 2026* We introduced key enhancements to the schema context for AI agents, expanded the media capabilities, and resolved some issues. For the schema context, the MCP server exposes additional field-level metadata: - Default values - Min/max field constraints - Complete enum option lists For media support, you can now upload all asset types (images, documents, audio, and video) up to 10 GB. For more details, checkout the [MCP server docs](https://docs.prepr.io/prepr-mcp-server/release-notes#added-schema-settings-exposed-to-the-llm). ## GraphQL responses now 25% faster for complex schemas *July 20th, 2026* We optimized schema initialization for environments with large and complex content models. This reduces request overhead and improves response-time consistency, benchmarks showing over 25% faster responses. Check out the full [GraphQL API reference](https://docs.prepr.io/graphql-api). ## GitHub schema syncs are now up to 25× faster *July 13th, 2026* We’ve optimized GitHub schema downloads by processing files concurrently. Schema validation, imports, and dry runs now complete significantly faster, especially for schemas containing many models and components. For more details on the GitHub schema sync process, check out the [Syncing a schema guide](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema#github-schema-sync). ## New retranslate option for content items *July 9th, 2026* The new **Retranslate** option lets you instantly replace the language variant text in a content item from a source language. For example, when the source language variant has a lot of changes since the first translation. ![retranslate option](https://assets-site.prepr.io/1rtde4hw9046//retranslate-option.png) This means you save time and maintain consistency with a single click of a button instead of using workarounds or manually translating. Check out the [localizing content guide](https://docs.prepr.io/content-management/localizing-content#retranslate-a-language-variant) for more details. ## Duplicating content items *July 9th, 2026* You can now duplicate a content item directly in the content item detail page, allowing you to create multiple similar content items with the same components like landing pages. ![duplicate content item](https://assets-site.prepr.io/7kb2qupzoci3//duplicate-content-item.png) This means you can focus purely on updating the copy and visuals. Check out the [content management guide](https://docs.prepr.io/content-management/managing-content/managing-content-items#duplicate-a-content-item) for more details. ## Improved AI text assistant for better results *July 7th, 2026* We've improved our AI features for creating content. Now you can, - use AI to improve text in the dynamic content editor - use your own prompts to improve text - continue prompting to fine-tune your initial request ![Ask AI improvements Dynamic content example](https://downloads-site.prepr.io/3kqz5ensr04m-ask-ai-improvements.gif) This update creates a more consistent AI experience so you get better results and speed up your editing workflow. In other words, spend less time editing and more time publishing. Check out the [AI text assistant guide](https://docs.prepr.io/ai-text-assistant) for more details. ## Environment-scoped preview URLs in local schemas *July 7th, 2026* You can now assign preview URLs to specific environments directly in local schemas. ![Setting preview URLs](https://assets-site.prepr.io/2nb5y9fe6hc1//preview-urls-local-schema-example.png) This makes managing preview URLs across your DTAP workflow much easier. For example, configure all environment-specific preview URLs in Development, sync your schema, and the environment assignments remain intact, no need to reconfigure preview URLs after syncing to Production. Check out the [model settings](https://docs.prepr.io/content-modeling/managing-models#preview) for more details. ## New remote source spec to validate your custom endpoint *July 2nd, 2026* We’ve just released a [new spec for custom remote sources](https://github.com/preprio/remote-source-validation). Now, you can automatically validate your custom endpoint based on its response format. This way you make sure you release a Prepr-compatible custom endpoint with fewer mistakes. For more details, check out the [custom remote source guide](https://docs.prepr.io/content-modeling/creating-a-custom-remote-source#validate-the-custom-remote-source). ## Improved subscription page gives you more clarity on usage *June 25th, 2026* With the improved subscription page, you can easily view your usage at a glance and enable notifications when your usage hits a threshold. ![Example subscription page](https://assets-site.prepr.io/4qi3w7pbogs3//subscription-page-highlight-notify-me.png) This gives you better visibility and control for total peace of mind. For more details, check out the [subscription guide](https://docs.prepr.io/project-setup/managing-your-subscription). ## New tracking code fallback to record view events *June 23rd, 2026* Previously, the Prepr tracking code only recorded view events by identifying content items with the specific `id` meta tag. Now, it also automatically records view events by matching the URL path (slug) to the correct content item when the meta tag is not present. This update prevents data gaps caused by accidental omission of the `id` meta tags in your front-end code, ensuring your analytics remain complete. Check out the [recording events guide](https://docs.prepr.io/data-collection/recording-events#view) for more details. ## Prepr MCP server public beta now live *June 22nd, 2026* Thanks to our users who've tried out the closed beta release of the Prepr MCP server we've made improvements and can provide [real-life use cases](https://docs.prepr.io/prepr-mcp-server/use-cases). Here are some of the enhancements: - Migrated server domain to `https://mcp.prepr.io` - Added OAuth support for secure agent connection - AI agent scopes mirror your authenticated user role permissions For a full list of updates since the closed beta, checkout the [release notes](https://docs.prepr.io/prepr-mcp-server/release-notes). To get started and connect your own AI agent, check out the [full MCP docs](https://docs.prepr.io/prepr-mcp-server). ## Case-insensitive personalization for UTM values *June 22th, 2026* We’ve improved how UTM values are matched in segment conditions. Previously, UTM values required an exact case match. For example, a condition targeting SpringCampaign would not match springCampaign. Now, UTM values are matched case-insensitively, making visitor segmentation and personalization more intuitive. This improvement helps prevent unexpected results caused by inconsistent UTM capitalization across campaigns and marketing tools. ## Password leak detection across authentication flows *June 17th, 2026* We've implemented leaked credential detection across all key authentication flows: - Sign-in - Password reset - Password creation after profile invite During these processes credentials are validated against breach intelligence, and users are required to reset their password if a compromised password is detected. ![example leaked password check](https://assets-site.prepr.io/5sxn8pdj784g//leaked-password-check-example.png) This improvement strengthens account security by preventing the use of exposed credentials that are known from external data breaches. By proactively detecting and blocking leaked passwords, we reduce the risk of credential stuffing attacks and unauthorized account access, significantly improving protection for user accounts and overall platform security. Check out [this blog post](https://blog.cloudflare.com/helping-keep-customers-safe-with-leaked-password-notification/) for more details. ## Introducing the Jotform integration *June 17th, 2026* With the new integration to this online form and surveys platform, you can embed Jotform forms directly into your content items. ![example content item](https://assets-site.prepr.io/1x01fxgq9zu2//example-jotform-form.png) This means you keep your content and related Jotform forms in one place. Check out the [Jotform integration guide](https://docs.prepr.io/integrations/jotform) for more details. ## Introducing the ActiveCampaign integration *June 5th, 2026* With the new integration to this email marketing platform, you can embed ActiveCampaign forms directly into your content items. ![example content item](https://assets-site.prepr.io/58zmwkfuphcg//example-content-item-with-activecampaign-form-selector.png) This means you keep your content and related ActiveCampaign forms in one place. Check out the [ActiveCampaign integration guide](https://docs.prepr.io/integrations/activecampaign) for more details. ## Improved Algolia integration for more control over indexing *June 3rd, 2026* We’ve overhauled the Algolia integration to give you total control over how Algolia indexes your content. Instead of relying on hidden internal rules, you can now use GraphQL queries to explicitly define exactly which model fields sync to Algolia and precisely when to generate separate records. ![Algolia integration setup with index query](https://assets-site.prepr.io/1n27zjxkam5w//algolia-search-index-example.png) Now you have full control over the data flow, ensuring search results are more relevant and streamlining your Algolia index size by syncing only what you actually need. Check out the [Algolia setup guide](https://docs.prepr.io/integrations/algolia) for more details. ## Webhook events updated with locale info *June 2nd, 2026* We've updated two webhook events with locale info to give you more accurate automation and clearer visibility into content item changes and deletions. 1. Previously, the `content_item.deleted` event only triggered for an entire content item deletion. Now, this event also fires when a single language variant of a content item is deleted. You can differentiate between a full deletion and a partial (locale-specific) deletion using the following parameters: - `scope`: Values can be either `item` (full deletion) or `locale` (partial deletion). - `locale`: If the `scope` value is `locale`, this value is the locale string, for example: `en-US`. 2. Previously, the `content-item.changed` event did not indicate which language variant of a content item was actually changed. Now, this event fires for every language variant updated. The `locale` parameter value indicates exactly which is updated, for example: `en-US`. > **Note:** > > These changes will only be available for new webhooks created after 1 June 2026. Check out the [webhooks guide](https://docs.prepr.io/development/best-practices/webhooks) for more details. ## Choosing environments to search shared content items *May 28th, 2026* Previously, when you added searchable models to a *Stack* or *Content reference* field in a shared model, you could either choose the model to be available to all environments or a single environment. Now, you can choose specific environments for searchable models. For example, when editors need to search content items common to multiple sites, but shouldn't view or include test content items. ![content reference example](https://assets-site.prepr.io/74tbexkhl4m1//content-reference-example.png) This means a cleaner editor experience because they see exactly what they need. Check out the [Shared schema guide](https://docs.prepr.io/project-setup/architecture-scenarios/shared-schema#define-environments-for-allowed-models) for more details. ## Environment-scoped preview for shared model *May 13th, 2026* Previously, when you set up preview URLs in a shared model, editors in any environment in the organization could see the full list of preview URLs you added. Now, you can scope preview URLs to individual environments when setting them up. ![environment-scoped preview setup](https://assets-site.prepr.io/30l2j00dl55j//organization-level-preview-setup-example.png) This cleans up the UI for visual editing as editors only see preview options relevant to their environment, saving them time and avoiding unnecessary scrolling and clicking wrong preview options. For more details, check out the [preview setup guide](https://docs.prepr.io/project-setup/setting-up-previews-and-visual-editing#set-up-preview-urls). ## AI-generated text for images *May 7th, 2026* Based on your feedback on the [Beta release](#ai-generated-alt-text-for-images-beta-release), we’ve officially launched AI-generated text for images. When uploading new images get AI-generated alt text, descriptions, or comma-separated keywords instantly. ![Example image with AI text values](https://downloads-site.prepr.io/4nmdjrif3sqj-generated-ai-text-for-uploaded-image.gif) This means you save time on manual entry and boost SEO rankings without the extra effort. Check out the [AI integration guide](https://docs.prepr.io/integrations/image-processing/ai) to activate this feature. ## Uploading files without an official MIME type *April 24th, 2026* Previously, when you uploaded a file without an official MIME type, this file could not be processed. For example, files with a `.bgi` extension. We've updated the file upload to allow files without an official MIME type, using the file extension to set a fallback [MIME type](https://mimetype.io/all-types). Also, when a MIME type has multiple extensions, we keep the original extension of the file. This means your files remain in their original format, ready to use in content items and processed. For more details, check out the [uploading assets](https://docs.prepr.io/content-management/managing-assets/managing-assets#uploading-assets) doc for more details. ## Introducing the Propeller commerce integration *April 17th, 2026* The new *Propeller* integration allows your team to easily include Propeller products in content items. ![propeller products example](https://assets-site.prepr.io/7tb7xmi3kvl//propeller-example-products.png) This means content editors can work more efficiently and error-free by simply choosing the relevant Propeller entries directly in Prepr. For more details, check out the [Propeller integration guide](https://docs.prepr.io/integrations/propeller). ## Defining percentage-based field widths *April 16th, 2026* With a new option to set field width, we've replaced the field columns fixed structure with a more flexible percentage-based option for model and component fields. ![set field width example](https://assets-site.prepr.io/53blx8g8hj0k//set-field-width.png) This visual improvement means editors can more easily and quickly scan and edit text fields in content items. > **Note:** > > If your schema setup included columns, you'll still see where these columns were applied in models and components. > This way you can double check the set up and update the field widths, if needed. You can then remove the column placeholders. Check out the [models guide](https://docs.prepr.io/content-modeling/managing-models#defining-field-width) for more details. ## Improved schema sync visibility *April 16th, 2026* We've updated the schema UI and since syncing is an important schema action, we've moved the sync schema action to the top of the page. ![GitHub schema sync example](https://assets-site.prepr.io/f4zxrsatpu9//github-sync-out-of-sync-example.png) With this update, you also have clarity on which repo, branch and commit the schema was last synced with and whether the schema is in sync with the branch or vice versa. Check out the [schema sync guide](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema#check-the-result) for more details. ## New field setting to declutter content item filter menus *April 16th, 2026* With the new **Show in filters** setting, developers can declutter the editing experience by hiding *List*, *Stack* and *Content reference* fields from the content item filter menus. ![show in filter setting](https://assets-site.prepr.io/11913k2evzxa//show-in-filter-setting.png) It’s about reducing noise and helping your editors focus on the data that actually matters. For example, when editors search *Product* content items, the product rating (list field) doesn't add value to the search because it would return too many results. Check out the field settings of the [stack field](https://docs.prepr.io/content-modeling/field-types#stack-field), [list field](https://docs.prepr.io/content-modeling/field-types#list-field) or [content reference field](https://docs.prepr.io/content-modeling/field-types#content-reference-field) for more details. ## Searching through your schema instantly *April 16th, 2026* Find your models, components, enumerations, and remote sources instantly with the new schema search bar. ![schema search](https://downloads-site.prepr.io/6hr5azh31shz-search-schema-no-feedback.gif) This way you stay focused on building your schema rather than searching. Check out the [models guide](https://docs.prepr.io/content-modeling/managing-models#search-models) for more details. ## Improved commenting on content items *March 31st, 2026* You can now add content item comments to specific fields and for a specific locale of the content item, making it possible to provide more granular and actionable feedback. ![Add comment example](https://assets-site.prepr.io/6qq13kb1u068//add-comment-example.png) This means you can follow your editing review process directly in Prepr speeding up your team collaboration and publication of content items. Check out the [collaboration guide](https://docs.prepr.io/content-management/collaboration#commenting) for more details. ## Manage content with AI agents using the new Prepr MCP server *March 27th, 2026* With the Prepr MCP server, you can create AI agents to manage content in your Prepr CMS environment with natural language prompts. For example, editors who usually write article posts in a tool like Notion can ask the AI agent to create content items for these posts. ![example AI agent chat](https://assets-site.prepr.io/5a36ghd8yn3j//chat-example.png) This means you can automate the grunt work so your team can focus on more strategic tasks. > **Note:** > > While in beta, Prepr will supply an MCP token to beta users. Contact [Prepr support](mailto:support@prepr.io?subject=Request%20Prepr%20MCP%20token) if you're interested to try it out. Check out the [Prepr MCP server overview](https://docs.prepr.io/prepr-mcp-server) to learn how to set it up for AI agents. ## Managing custom event types *March 17th, 2026* You can now set the **Display name** and the **Visibility** of custom event types directly in Prepr. For example, to change a technical event type name `Scrolled50` to something like *Scrolled 50% of the page* or to hide old events with an outdated event type. ![Event type list](https://assets-site.prepr.io/2lx4vy8vn3ed//custom-event-type-settings.png) This means that you and your marketers get a clear clutter-free view of events and only see relevant metrics. Check out the [Event type settings](https://docs.prepr.io/data-collection/recording-events#managing-custom-event-types) for more details. ## Expanded trigger criteria for *content\_item.invalidated* webhook event *March 12th, 2026* We’ve updated the criteria for the `content_item.invalidated` webhook event to provide better synchronization between your external systems and published content. Previously, this event was triggered for external system changes to a remote source item. Now, it also triggers for [API `PATCH`](https://docs.prepr.io/mutation-api/content-items-create-update-and-destroy#patch-a-content-item) updates. This means if you update a published content item via the API `PATCH` request, the `content_item.invalidated` event will fire automatically. Check out the [webhooks guide](https://docs.prepr.io/development/best-practices/webhooks#content-item-events) for more details. ## Fixed validation for required fields after hidden conditional sections *March 12th, 2026* We fixed an issue where required fields following a conditional section were incorrectly ignored when that section was not shown. Previously, this could allow content to be published without completing required fields. With this fix, required fields after a hidden conditional section are now validated correctly before publication. ## Schema validation based on new Prepr schema spec *March 11th, 2026* We've just released a [Prepr schema validation](https://github.com/preprio/action-schema-validation) using the new [Prepr schema spec](https://github.com/preprio/action-schema-validation/blob/main/spec/2026-03-05.json5). Now, you can automatically validate your schema JSON files in *GitHub* to ensure you import a valid Prepr schema every time. This release lays the foundation for the next steps to use AI to generate a fully validated Prepr schema. For more details, check out the [schema validation guide](https://docs.prepr.io/development/working-with-cicd/validating-a-schema). ## Prepr Next.js package version 2.2.5 is available *March 10th, 2026* We've just released a new version of the Prepr Next.js package. Version 2.2.5 includes a manual override of the `fast-xml-parser` to solve the `CVE-2026-25128` DoS vulnerability. We strongly recommend you upgrade your installation of the Prepr Next.js package, where applicable. For more details, check out the [Prepr Next.js package guide](https://docs.prepr.io/prepr-nextjs-package). ## Remote source settings preserved in schema sync *March 5th, 2026* We’ve updated the behavior of the *Direct schema sync* and the schema import (via *GitHub*, *GitLab*, *Bitbucket* or *Azure DevOps*) for existing remote sources. Previously, an existing remote source was always fully synced. From now on, the URL, headers, and image domains of an existing remote source will no longer be updated during a schema sync and will be ignored instead. This matches the current behavior of *Preview URLs* and helps prevent manual rework after each sync, especially when test or development URLs need to remain unchanged. Check out the [Schema sync guide](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema) for more details. ## New Prepr Next.js package version 2.2.x is available *March 3rd, 2026* The newest version of the Prepr Next.js package now automatically detects and handles stega encoding. Stega encoding uses hidden characters so editors can click and edit content directly from the preview. This update means you no longer need to write extra code to clean up these hidden characters which could cause misalignment of some UI elements in the live preview. Check out the [Prepr Next.js package guide](https://docs.prepr.io/prepr-nextjs-package) for more details. ## Legacy API deprecation on August 1st, 2026 *February 27th, 2026* As part of our effort to streamline our API infrastructure, the legacy Prepr API endpoint `api.eu1.prepr.io` will expire and be removed on August 1st, 2026. If your application currently uses `api.eu1.prepr.io`, update your implementation before then. For write operations (mutations), update your endpoint to `mutation.prepr.io`. For read-only operations (queries), we strongly recommend updating your endpoint to `cdn.prepr.io`. > **Note:** > > Other Prepr domains (such as `customers.prepr.io`) remain unchanged. > This change does not affect your data or application logic, only the API domain being used. Contact [Prepr support](mailto:support@prepr.io) if you have any specific questions. ## AI-generated alt text for images (Beta release) *February 25th, 2026* When uploading new images you can now get AI-generated text values instantly. For example: alt text, specific descriptions, or comma-separated keywords. This means you save time on manual entry and boost SEO rankings without the extra effort. ![Example image with AI text values](https://downloads-site.prepr.io/4nmdjrif3sqj-generated-ai-text-for-uploaded-image.gif) Contact [Prepr support](mailto:support@prepr.io?subject=Request%20AI%20activation%20for%20image%20text%20values) to activate AI-generated text for images in your environment. ## Improved linked content visibility *February 20th, 2026* With the improved linked content visibility, you can now get a high-level view of linked content items from the content item list. ![view linked content items gif](https://downloads-site.prepr.io/70kmn43zrvy1-view-linked-content-items.gif) This removes the need to open individual items to investigate dependencies and therefore speeds up your review of linked content items. For more details, check out the [content management doc](https://docs.prepr.io/content-management/managing-content/managing-content-items#view-linked-content-items). ## SSO-ready invites speed up sign-ins *February 12th, 2026* Inviting new users to Prepr is now smoother when you use SSO, for example, *Microsoft Entra ID* or *SAML 2.0*. As an administrator, you can now assign roles upfront by simply inviting the new SSO user. This means users no longer need to wait for an admin to grant access after their first SSO sign-in. When the user clicks the **Activate** link in the invite email, Prepr automatically routes them to the correct SSO sign-in flow (instead of email + password), so they can start working right away. Check out the [managing users guide](https://docs.prepr.io/project-setup/managing-users#send-invitation-to-sign-in) for more details. ## New webhook event for remote source item changes *February 10th, 2026* We’ve added a new event to our webhooks: `content_item.invalidated`. This new event triggers whenever remote source data linked to a content item changes. This means you can now set up a webhook to notify your front end the moment the remote source data becomes stale. For more details, check out the [webhooks guide](https://docs.prepr.io/development/best-practices/webhooks#content-item-events). ## Additional granular permissions to manage content items *February 10th, 2026* In addition to the [action-based content permissions](https://docs.prepr.io/changelog/2025#introducing-granular-permissions-to-manage-content-items) we released recently, you can now extend user role permissions and content item access with the following additional features: - *Direct content item sharing* - You can now grant specific users access to individual content items. This allows you to easily work with freelancers or agencies while maintaining strict data privacy. ![share item option](https://assets-site.prepr.io/98ujghrpwr//share-one-content-item.png) - *On-demand access requests* - When users try to open a linked content item they don't have access to, they can directly request access within Prepr with this new workflow. ![Request access to linked author content item](https://assets-site.prepr.io/36uc3o43vtuo//request-access-workflow.png) - *Show my items only* - You have a new setting to allow a user to only access content items they've created or items explicitly shared with them. For example, a freelance editor who only needs to access content items they're assigned to work on. ![User role - Items related to user option](https://assets-site.prepr.io/1xjumxhyxr6e//organization-user-role-items-related-to-user-only-option.png) - *Scoped content access* - Allows users to access only content items in specific workflow stages. For example: A translator who only needs to access content items in the *Review* or *Translation* workflow stages. ![Content restrictions - German translator example](https://assets-site.prepr.io/ddhyi2zmysr//content-restrictions-german-translator.png) These new access right features enhance data security, prevent accidental errors and increase user focus for certain roles, like freelance editors. Check out the [user role guide](https://docs.prepr.io/project-setup/managing-roles-and-permissions#content-management-permissions) for more details. ## Introducing the ProspectPro integration *January 12th, 2026* With the new ProspectPro integration, you can easily connect Prepr to this B2B prospecting platform. This integration lets you segment website visitors based on their company industry and company size. ![ProspectPro segment example](https://assets-site.prepr.io/30x5go0v7mi4//customer-segment-example.png) By segmenting visitors this way you can personalize content for an enhanced user experience for your B2B audience. Check out the [ProspectPro integration guide](https://docs.prepr.io/integrations/prospectpro) for more details. ## Activate integration with Prepr directly in HubSpot Marketplace *January 6th, 2026* We've introduced a streamlined authorization flow that allows you to activate the integration with Prepr directly from the HubSpot Marketplace. This makes the process seamless and fully contained within the HubSpot environment. ![Prepr app in HubSpot Marketplace](https://assets-site.prepr.io/14ojkq3xkuni//prepr-app-in-hubspot-marketplace.png) Check out the [HubSpot integration guide](https://docs.prepr.io/integrations/hubspot) for more details. Source: https://docs.prepr.io/changelog --- # Prepr's product roadmap We’re constantly improving our products, integrations, and services. Learn about features we're working on and upcoming improvements. ## Q4 2026 & Q1 2027 ### Asset management with MCP server Enable AI agents to add, update, and delete assets in Prepr through the MCP server. ### Enhanced MCP server features and skills Expand the MCP server with broader capabilities, allowing AI agents to perform more complex tasks while maintaining secure access control. Add reusable skills that package common workflows into higher-level actions, making AI integrations more powerful and easier to use. ### Semantic search in UI and MCP Server Search content items using natural language, making it much easier to find the right content across large content repositories. ### New metrics & analytics tab Introduce a dedicated metrics and analytics experience that provides an overview of the overall impact of goals and experiments. Add in-depth metrics to individual content items and experiments, making it easy to understand performance at both an overall and granular level. These metrics form the foundation for *AI Insights* and *AI Suggestions*, creating a logical flow from understanding performance → identifying what we’ve learned → getting actionable recommendations → improving content and experiments. ### Enhanced segment options by scroll depth and time spent Expand segment creation with behavioral criteria such as the time visitors spend on the website and their average scroll depth on specific pages. This enables more granular audience segmentation based on visitor engagement. ### Automated personalization Automatically create audience segments based on website and visitor events. Use these segments to automatically create personalized content elements and run experiments, reducing the manual effort required to set up personalization. ### AI Insights Introduce a generic AI Insights capability across the platform. AI will analyze metrics and performance data to identify what teams have learned from their goals, experiments, and other measurable outcomes. ### AI Suggestions Turn insights into actionable recommendations. AI will suggest what teams should add, do more of, stop doing, or keep doing, and help transform these recommendations into impactful content items and experiments. ### Multi-select enumerations Allow enumeration fields to support multiple selected values instead of being limited to a single option. Editors can use this to assign multiple categories, content types, product features, or labels without creating additional fields or complex reference structures. ### Improved experiment overview and setup guidance Redesign the experimentation experience to make creating and managing experiments more intuitive. During setup, editors receive clearer guidance around required tracking, success metrics, traffic allocation, and experiment configuration. The experiment overview will also provide better visibility into experiment status, health, configuration quality, and expected next steps. ### AI-first content management We’re exploring an AI-first experience where the *Content* page becomes a conversational workspace instead of a traditional content list. Users can simply describe what they’re looking for or what they want to accomplish, while AI helps locate content, answer questions, and guide them through editorial workflows. ### AI-powered GEO and AEO content checks We’re expanding content checks with AI-powered guidance focused on Generative Engine Optimization (GEO) and Answer Engine Optimization (AEO). Rather than only validating traditional SEO best practices, Prepr will help editors create content that’s better suited for AI search, knowledge retrieval, and modern discovery platforms. The AI assistant provides recommendations around content structure, completeness, clarity, and semantic quality, helping teams produce content that’s easier to index, understand, and surface across both search engines and AI-powered experiences. ### Automatic redirects for slug changes Changing a page slug should never result in broken links. Prepr will automatically generate redirects whenever an editor manually changes the slug of a published content item. This eliminates the need to manually configure redirects after URL changes, preserving existing links, maintaining search engine rankings, and ensuring visitors are seamlessly redirected to the updated page. ### Ongoing editorial improvements We’re continuing to refine the day-to-day editorial experience with a series of productivity enhancements across the CMS. These include word counters for text fields, simplified *Stack* editing, and various usability improvements throughout the editor. Additional enhancements include click-to-edit capabilities and commenting support within the *Prepr Toolkit*, more flexible asset validation, and streamlined content creation workflows. While individually small, these improvements collectively reduce friction and help editors work faster and more efficiently. ### Improved CRM/CDP integrations for audience segmentation We’re strengthening CRM and Customer Data Platform integrations to make external audience segmentation a first-class part of adaptive content delivery. Editors will be able to use audience segments that already exist in connected CRM and CDP platforms, eliminating the need to recreate segmentation rules inside Prepr. By combining external customer intelligence with Prepr’s personalization capabilities, organizations can deliver more relevant content experiences based on campaign audiences, customer lifecycle stages, lead status, or other existing segmentation strategies. This creates a more seamless personalization workflow while maximizing the value of existing marketing technology investments. > **Note:** > > The list of features above is part of our current product plan and some future items on our roadmap. > In line with the flexible nature of our development and the Prepr culture of keeping up with the latest technology and trends, the estimated timelines and details mentioned here might change. > Check out our [Changelog](https://docs.prepr.io/changelog) to stay updated. > **Warning:** > > **Disclaimer** \ > > Please note that these items are for informational purposes only and are not a binding commitment. > The development, release and timing are subject to change. > So, don't rely on this information when making purchasing decisions, but do call us for questions you might have on any of these features. Source: https://docs.prepr.io/roadmap --- # Setting up your production-ready project *Welcome to Prepr, a data-driven headless CMS with a built-in personalization engine and optimization features. Learn about the different parts that you need to set up your project with Prepr and the steps to follow to make the best use out of its features for your front-end applications.* If you can't wait to dive straight into Prepr, follow the [Quick start guide](https://docs.prepr.io/quick-start-guide) to get your feet wet. Learn more about the structure of your Prepr project and then move on to the step-by-step guide to set it up. - [Step-by-step guide](https://docs.prepr.io/project-setup/step-by-step-guide) - [Prepr overview](https://docs.prepr.io/project-setup/prepr-overview) Looking for something specific? Check out the detailed resources below. - [Architecture scenarios](https://docs.prepr.io/project-setup/architecture-scenarios) - [Setting up environments](https://docs.prepr.io/project-setup/setting-up-environments) - [Migrating content](https://docs.prepr.io/project-setup/migrating-content) - [Managing users](https://docs.prepr.io/project-setup/managing-users) - [Managing roles and permissions](https://docs.prepr.io/project-setup/managing-roles-and-permissions) - [Setting up SSO](https://docs.prepr.io/project-setup/setting-up-sso) - [Managing your subscription](https://docs.prepr.io/project-setup/managing-your-subscription) Source: https://docs.prepr.io/project-setup --- # Content modeling *Explore the resources below to get started with content modeling and learn how to set up a well-defined schema in Prepr CMS.* Before diving into Prepr, learn the basics about content modeling and how to model content using some typical examples like a *Blog*, *Page* and *Personalization*. - [Fundamentals](https://docs.prepr.io/content-modeling/fundamentals) - [Examples](https://docs.prepr.io/content-modeling/examples) - [Best practices](https://docs.prepr.io/content-modeling/best-practices) > **Note:** > > If you'd like to discuss what you've learnt and how to apply these concepts to your project, [register for a Content modeling workshop](https://prepr.io/resources/content-modeling-webinar). Dive into Prepr and learn how to set up a schema by managing models, components, setting up remote sources and other more advanced features. - [Managing models](https://docs.prepr.io/content-modeling/managing-models): Learn how to create, edit, and delete a model, and manage model settings. - [Field types reference](https://docs.prepr.io/content-modeling/field-types): Check out a list of all available field types in Prepr. - [Defining the Asset model](https://docs.prepr.io/content-modeling/defining-the-asset-model): Learn how to add fields and enable localization for assets. - [Managing components](https://docs.prepr.io/content-modeling/managing-components): Learn how to how to create and manage a component. - [Managing enumerations](https://docs.prepr.io/content-modeling/managing-enumerations): Learn how to how to create and manage an enumeration. - [Setting up a built-in remote source](https://docs.prepr.io/content-modeling/setting-up-a-built-in-remote-source): Learn how to set up a predefined remote source to reference content from another system. - [Creating a custom remote source](https://docs.prepr.io/content-modeling/creating-a-custom-remote-source): Learn how to set up a custom remote source to reference content from another system. Source: https://docs.prepr.io/content-modeling --- # Connecting a front-end framework The flexibility of a data-driven headless CMS allows you to connect your favorite front-end framework, deliver content and optimize and personalize the visitor journey. > **Note:** > > Help shape Prepr by sharing your Prepr development experience, asking questions and learning from other developers in our [developer roundtable](https://prepr.io/resources/developer-roundtable). - [Next.js](https://docs.prepr.io/connecting-a-front-end-framework/nextjs) - [Astro](https://docs.prepr.io/connecting-a-front-end-framework/astro) - [Nuxt](https://docs.prepr.io/connecting-a-front-end-framework/nuxtjs) - [Svelte](https://docs.prepr.io/connecting-a-front-end-framework/svelte) - [Laravel](https://docs.prepr.io/connecting-a-front-end-framework/laravel) - [React](https://docs.prepr.io/connecting-a-front-end-framework/react) - [Vue.js](https://docs.prepr.io/connecting-a-front-end-framework/vuejs) - [Angular](https://docs.prepr.io/connecting-a-front-end-framework/angular) - [Node.js](https://docs.prepr.io/connecting-a-front-end-framework/nodejs) - [PHP](https://docs.prepr.io/connecting-a-front-end-framework/php) - [Using an SDK](https://docs.prepr.io/connecting-a-front-end-framework/using-an-sdk) Source: https://docs.prepr.io/connecting-a-front-end-framework --- # Developing with Prepr CMS *Discover everything you need to know to develop with Prepr CMS, including resources for connecting front-end frameworks, best practices, managing CI/CD pipelines and integration guides.* > **Note:** > > Help shape Prepr by sharing your Prepr development experience, asking questions and learning from other developers in our [developer roundtable](https://prepr.io/resources/developer-roundtable). - [Fundamentals](https://docs.prepr.io/development/fundamentals): Check out the fundamentals you need to start your development with Prepr CMS. - [Best practices](https://docs.prepr.io/development/best-practices): Check out best practices when developing with Prepr CMS such as rendering strategies, using typescript, handling SEO, etc. . - [Working with CI/CD](https://docs.prepr.io/development/working-with-cicd): Learn about Prepr CMS features that sync a schema and content between environments to support your CI/CD process. - [Integrations](https://docs.prepr.io/integrations): Learn how to integrate Prepr CMS for ecommerce content, implementing a search engine, single sign-on and more. Source: https://docs.prepr.io/development --- # Content management Discover all you need to know about managing content items, how to handle assets, localizing content, and collaboration when working with content items in Prepr CMS. - [Managing content](https://docs.prepr.io/content-management/managing-content): Learn how to manage content items, including creating rich content and improving SEO. - [Managing assets](https://docs.prepr.io/content-management/managing-assets): Learn how to upload and manage assets like images, videos and other files. - [Reviewing content](https://docs.prepr.io/content-management/reviewing-content): Review content items and identify issues that need attention. - [Measuring content impact](https://docs.prepr.io/content-management/measuring-content-impact): Measure the reach, engagement and performance of your content items. - [Localizing content](https://docs.prepr.io/content-management/localizing-content): Learn how to work with content items and assets in different languages. - [Collaboration](https://docs.prepr.io/content-management/collaboration): Learn about content item workflows to collaborate with other content editors. Source: https://docs.prepr.io/content-management --- # Data collection *Prepr CMS offers several data-driven features such as Adaptive content, A/B testing, and Recommendations. To power these features, Prepr requires visitor data. This data is essential for creating segments for personalization, evaluating A/B test results, and determining relevant recommendations. Discover all you need to know about collecting and managing visitor data to make the most out of these features.* Learn more about data collection key concepts and move on to the step-by-step guide to set it up. - [Fundamentals](https://docs.prepr.io/data-collection/fundamentals) - [Step-by-step guide](https://docs.prepr.io/data-collection/step-by-step-guide) Looking for something specific? Check out the detailed resources below. - [Setting up tracking](https://docs.prepr.io/data-collection/setting-up-the-tracking-code): Learn how to set up event tracking in your web app. - [Recording events](https://docs.prepr.io/data-collection/recording-events): Learn how to record events using the Prepr Tracking Code. - [Managing visitor data](https://docs.prepr.io/data-collection/managing-visitor-data-manually): Learn how to manage visitor data in Prepr CMS. - [Tracking data using API](https://docs.prepr.io/data-collection/tracking-data-using-the-api): Learn how to track event data using the REST API. - [Privacy & Security](https://docs.prepr.io/data-collection/privacy-and-security): Check out a privacy and security conditions for data in Prepr CMS. Source: https://docs.prepr.io/data-collection --- # Personalization *Discover all you need to know about making your website adaptive by setting up personalization, managing segments and creating adaptive content to improve engagement and user experience.* - [Setting up personalization](https://docs.prepr.io/personalization/setting-up-personalization): A developer's guide to set up personalization in your front end. - [Defining conversion goals](https://docs.prepr.io/personalization/defining-goals): Learn how to define goals to measure in-depth conversions for your experiments. - [Managing segments](https://docs.prepr.io/personalization/managing-segments): Learn how to group your visitors into precise segments for personalization. - [Managing adaptive content](https://docs.prepr.io/personalization/managing-adaptive-content): Learn how to create and adjust adaptive content. Source: https://docs.prepr.io/personalization --- # A/B testing *Discover all you need to know about setting up and using Prepr CMS A/B testing to improve engagement and user experience.* - [Setting up A/B testing](https://docs.prepr.io/ab-testing/setting-up-ab-testing): A developer's guide to set up A/B testing in your front end. - [Running A/B tests](https://docs.prepr.io/ab-testing/running-ab-tests): Learn how to create and run A/B tests on your content. Source: https://docs.prepr.io/ab-testing --- # Setting up recommendations Estimated duration: 15-30 minutes ## Introduction Prepr allows you to add recommendations to your web application quickly. This tutorial demonstrates how to deliver recommendations using the Prepr GraphQL API. Follow the step-by-step guide below. ## Use case Deliver highly relevant content recommendations to the visitors of your websites and apps using the GraphQL API to increase engagement. Let's examine an everyday use case: an article that you want to show recommendations for. ![drawing](https://assets-site.prepr.io/w_1920/f909ab3d-9118-4d55-a0ff-fc0d035f2919.jpg) Recommendations are usually displayed below an article to entice visitors to view more content. With Prepr, you can display three types of recommendations: - Similar items - People also viewed items - Popular items ### Similar items Similar items are recommendations similar to the article the visitor is currently viewing. Because it's on the same topic, or because it's by the same author, or maybe it's approximately the same length. With this recommendation type, the algorithm looks at the characteristics of the article (content, meta-data, links to other articles, etc.) and determines what the most relevant related articles are based on a score. ### People also viewed items People also viewed items are items that other visitors also viewed along with the item that a visitor is currently viewing. The algorithm looks at the current item and determines the visitors that viewed this item. It then lists the other items that these visitors also viewed. ### Popular items Popular items, the name says it all, are the most viewed items. The algorithm looks at how often an item has been viewed for this recommendation type. You could say this is not a recommendation but sorting by popularity. ### Content structure Before you start generating the recommendations, it’s a good idea to look at the content model of the article. That structure largely determines how accurate the recommendations become. ![Recommendations-popular-items](https://assets-site.prepr.io/w_1920/a54d44b2-34a0-4396-8fe8-5d2d7644018b.jpg) In this case you have an article with a number of content fields: title, intro and content. In addition, there are references to an author and to one or more categories. And finally, an editor can manually add tags. Prepr uses all this information to provide the most relevant recommendations. ## Creating a Prepr CMS account > **Note:** > > You can skip this step if you already have a Prepr CMS account with models and content items. Following this guide requires a (free) Prepr CMS account. - Go to https://signup.prepr.io/ and sign up - Follow the [quick start guide](https://docs.prepr.io/quick-start-guide) to add models and content items ## Querying the API for similar items To retrieve similar items, you must first have the ID of the content item where you want to show recommendations for ### Getting a content item ID - Go to the **Content** tab and open the content item for which you want to retrieve similar items. - Click the icon and choose the **Copy item ID** option. ![Get content item ID](https://assets-site.prepr.io/514uv9yt2crq//copy-content-item-id.png) ### Retrieving similar items - Click the icon and choose the **Access tokens** option. - Click to open the *GraphQL Production* token details. - Click the **Open in API explorer** link. ![GraphQL production access token example](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) View the API reference for all [API authentication details](https://docs.prepr.io/graphql-api/authorization) - Add the following query to the explorer: ```graphql copy query { Similar_Articles( id: "0cbe2455-124c-4820-b2ac-dcc4261e150c" ) { items { _id title } } } ``` - Replace the **id** value with the ID of your content item Note the **Similar\_Articles** query type at the beginning of the query. For each content model in your environment the Prepr creates a corresponding GraphQL type with a similarity algorithm. The plural type name of the content model is prefixed with Similar\_. For example Article generates a type Similar\_Articles. You can find all these options in the API Explorer. - Run the query The result should look something like: ```json copy { "data": { "Similar_Articles": { "items": [ { "_id": "885b71ac-5a4b-4d2e-9770-a4a6f20425e9", "title": "15 Tips On How To Brand Yourself Online" }, { "_id": "f1d9b142-883f-4964-8d8f-cd2f255330a2", "title": "Why Customization Is Key For Entrepreneurs In The Digital Age" }, { "_id": "dd42b8c4-4773-4fdd-a71f-87e57b35beaa", "title": "Building User Trust In UX Design" }, { "_id": "79c453ed-ffe2-4aec-8fb2-f549e3775264", "title": "Building A Video Streaming App With Nuxt.js" }, { "_id": "5a53b271-898e-4eef-b877-5863b34b6ff9", "title": "UI Design Testing Tools I Use All The Time" }, { "_id": "ca0e0d37-600b-42f7-a013-9b0f8e18f127", "title": "The Evolution Of Jamstack" }, { "_id": "dd3309eb-df84-4e4b-8fdf-80b31b8eb58a", "title": "The Rise Of Design Thinking As A Problem Solving Strategy" }, { "_id": "f4b65d11-c2e2-4320-a363-e7d420d03ed2", "title": "Modeling A GraphQL API For Your Blog" } ] } } } ``` That's all. You now have recommendations that you can show in your application. Let's see how we can improve the recommendations even more. ### Filtering the result The previous example included all items for determining recommendations. But often, you want to make the result even more accurate. For example, you may want to query only recommended articles published recently or in a particular category. So let's see how you can do that. - Expand the query with the following parameters: ```graphql copy query { Similar_Articles( id: "0cbe2455-124c-4820-b2ac-dcc4261e150c" where: { _publish_on_gt: "2021-01-01T00:00:00+00:00" categories: { _slug_any: [ "ux-design", "development" ] } }, limit: 3 ) { items { _id title } } } ``` - We added **\_publish\_on\_gt**: "2021-01-01T00:00:00+00:00" to show only articles published after January 1, 2021. Note the underscore at the beginning of the parameter. - We added categories: **`{ _slug_any: [ "ux-design", "development" ] }`** to limit the result to only articles in the “UX Design” or the “Development” categories. Note the square brackets because we’re dealing with an array. - We added **limit: 3** to limit the number of results to three items The result should look something like: ```json copy { "data": { "Similar_Articles": { "items": [ { "_id": "dd42b8c4-4773-4fdd-a71f-87e57b35beaa", "title": "Building User Trust In UX Design" }, { "_id": "5a53b271-898e-4eef-b877-5863b34b6ff9", "title": "UI Design Testing Tools I Use All The Time" }, { "_id": "dd3309eb-df84-4e4b-8fdf-80b31b8eb58a", "title": "The Rise Of Design Thinking As A Problem Solving Strategy" } ] } } } ``` View the API reference for [all filter options](https://docs.prepr.io/graphql-api/fetching-filtering-collections). ### Optimizing the recommendation algorithm The recommendation algorithm for similar items finds items based on the following criteria: |Criteria|Default weight|Description| |--------|----|------| |Topics |1.4| Highest weight. Topics are extracted automatically by the AI text analysis engine. It identifies specific people, places, or concepts within the item text and finds items with matching topics.| |Content References|1.2| Matches content items linked to the main item through any content references whether in the *Stack*, *Component* or *Dynamic content* field. For example, articles in the same category and articles by the same author.| |Tags|0.8| Matches items based on tag values in the main content item.| You can tweak the default logic to align with your own business priorities. For example, if your content items are related more by their common tags rather than linked items, you can increase the importance of *Tags*. Here’s an example of how that works: ```graphql copy query { Similar_Articles( id: "0cbe2455-124c-4820-b2ac-dcc4261e150c" where: { _publish_on_gt: "2021-01-01T00:00:00+00:00" categories: { _slug_any: [ "ux-design", "development" ] } }, limit: 3 rules: { entities: 0 tags: 1 references: 0.5 } ) { items { _id title } } } ``` - We added rules: **`{ entities: 0, tags: 1, references: 0.5 }`** to indicate that topics should not be included and that the tag criteria has higher priority than references. We recommend starting with the default setting and adding rules only if the result does not meet your expectations. View the [API reference](https://docs.prepr.io/graphql-api/personalization-recommedations-similar-content) for all rules options. ## Querying the API for People Also Viewed items To retrieve *People also viewed* items, you need the ID of the content item that you want to show recommendations for. ### Getting a content item ID - Go to the **Content** tab and open the content item you want to use as the reference. - Click the icon and choose the **Copy item ID** option. ![Get content item ID](https://assets-site.prepr.io/514uv9yt2crq//copy-content-item-id.png) Now that you have the *Content Item ID*, you can track visitors who viewed the same content item. That information can be used to display the *People Also Viewed* items. ### Capturing views You can capture view events in Prepr using a lightweight piece of JavaScript, the *Prepr Tracking Code*. Follow the steps in the [Tracking setup guide](https://docs.prepr.io/data-collection/setting-up-the-tracking-code#enabling-prepr-tracking) to add the *Prepr Tracking Code*. Once you've enabled tracking, you can then [add a meta tag](https://docs.prepr.io/data-collection/recording-events#tracking-content-items) to record view events on content items. Check out the [Events doc](https://docs.prepr.io/data-collection/recording-events) for all tracking options. ### Retrieving People also viewed items Now that you’re tracking the visitors that view an item, you can retrieve the other items that these visitors also viewed. - Click the icon and choose the **Access tokens** option to view all the access token. - Click to open the *GraphQL Production* token details. - Click the **Open in API explorer** link. ![GraphQL production access token example](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) View the API reference for all [API authentication details](https://docs.prepr.io/graphql-api/authorization) - Add the following query to the explorer: ```graphql copy query { PeopleAlsoViewed_Articles ( id : "90276002-d628-4ba6-b3c8-f756c486b67b" ) { items { _id, title } } } ``` Note the **PeopleAlsoViewed\_Articles** query type at the beginning of the query. Prepr automatically provides you with a `PeopleAlsoViewed` query for each model. For example, if your model name is Article, you also get the PeopleAlsoViewed\_Articles query. You can find all these query options in the API Explorer. The result should look something like this: ```json copy { "data": { "PeopleAlsoViewed_Articles": { "items": [ { "_id": "0cbe2455-124c-4820-b2ac-dcc4261e150c", "title": "How to set up a Google Ads account" }, { "_id": "dd42b8c4-4773-4fdd-a71f-87e57b35beaa", "title": "Building User Trust In UX Design" }, { "_id": "79c453ed-ffe2-4aec-8fb2-f549e3775264", "title": "Building A Video Streaming App With Nuxt.js" }, { "_id": "5a53b271-898e-4eef-b877-5863b34b6ff9", "title": "UI Design Testing Tools I Use All The Time" }, { "_id": "dd3309eb-df84-4e4b-8fdf-80b31b8eb58a", "title": "The Rise Of Design Thinking As A Problem Solving Strategy" }, { "_id": "f1d9b142-883f-4964-8d8f-cd2f255330a2", "title": "Why Customization Is Key For Entrepreneurs In The Digital Age" }, { "_id": "885b71ac-5a4b-4d2e-9770-a4a6f20425e9", "title": "15 Tips On How To Brand Yourself Online" }, { "_id": "ca0e0d37-600b-42f7-a013-9b0f8e18f127", "title": "The Evolution Of Jamstack" }, { "_id": "f4b65d11-c2e2-4320-a363-e7d420d03ed2", "title": "Modeling A GraphQL API For Your Blog" } ] } } } ``` ### Filtering the result The previous example includes all items that other users viewed. But often, you want to make the result even more accurate. For example, only show items published recently or in a particular category. So let's see how you can do that. - Expand the query with the following parameters: ```graphql copy query { PeopleAlsoViewed_Articles( id : "90276002-d628-4ba6-b3c8-f756c486b67b", where: { _publish_on_gt: "2021-01-01T00:00:00+00:00" categories: { _slug_any: [ "ux-design", "development" ] } }, limit: 3 ){ items { _id title } } } ``` - We added `_publish_on_gt: "2021-01-01T00:00:00+00:00"` to show only articles published after January 1, 2021. Note the underscore at the beginning of the parameter. - We added categories: `{ _slug_any: [ "ux-design", "development" ] }` to limit the result to only articles in the “UX Design” or the “Development” categories. Note the square brackets because you’re dealing with an array. - We added `limit: 3` to limit the number of results to three items. Result: ```json copy { "data": { "PeopleAlsoViewed_Articles": { "items": [ { "_id": "dd42b8c4-4773-4fdd-a71f-87e57b35beaa", "title": "Building User Trust In UX Design" }, { "_id": "79c453ed-ffe2-4aec-8fb2-f549e3775264", "title": "Building A Video Streaming App With Nuxt.js" }, { "_id": "5a53b271-898e-4eef-b877-5863b34b6ff9", "title": "UI Design Testing Tools I Use All The Time" } ] } } } ``` ## Querying the API for most popular items To show the most popular items, you must track how often visitors view content items. That information can be used to display the most popular items. ### Capturing views You can capture view events in Prepr using a lightweight piece of JavaScript, the *Prepr Tracking Code*. Follow the steps in the [Tracking setup guide](https://docs.prepr.io/data-collection/setting-up-the-tracking-code#enabling-prepr-tracking) to add the *Prepr Tracking Code*. Once you've enabled tracking, you can then [add a meta tag](https://docs.prepr.io/data-collection/recording-events#tracking-content-items) to record view events on content items. Check out the [Events doc](https://docs.prepr.io/data-collection/recording-events) for all tracking options. ### Retrieving most popular items Now that you’re tracking how often visitors view an item, you can retrieve the most popular items. - Click the icon and choose the **Access tokens** option. - Click to open the *GraphQL Production* token details. - Click the **Open in API Explorer** link. ![GraphQL production access token example](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) View the API reference for all [API authentication details](https://docs.prepr.io/graphql-api/authorization) - Add the following query to the explorer: ```graphql copy query { Popular_Articles { items { _id title _views } } } ``` Note the **Popular\_Articles** query type at the beginning of the query. Prepr automatically provides you with a popularity query for each model. If your model name is Article, you also get the Popular\_Articles query. You can find all these query options in the API Explorer. Note the **\_views** system field that shows the number of views for a content item. This field is optional. We recommend not using it to optimize cache efficiency. The result should look something like this: ```json copy { "data": { "Popular_Articles": { "items": [ { "_id": "0cbe2455-124c-4820-b2ac-dcc4261e150c", "title": "How to set up a Google Ads account", "_views": 83 }, { "_id": "dd42b8c4-4773-4fdd-a71f-87e57b35beaa", "title": "Building User Trust In UX Design", "_views": 59 }, { "_id": "79c453ed-ffe2-4aec-8fb2-f549e3775264", "title": "Building A Video Streaming App With Nuxt.js", "_views": 49 }, { "_id": "5a53b271-898e-4eef-b877-5863b34b6ff9", "title": "UI Design Testing Tools I Use All The Time", "_views": 40 }, { "_id": "dd3309eb-df84-4e4b-8fdf-80b31b8eb58a", "title": "The Rise Of Design Thinking As A Problem Solving Strategy", "_views": 29 }, { "_id": "f1d9b142-883f-4964-8d8f-cd2f255330a2", "title": "Why Customization Is Key For Entrepreneurs In The Digital Age", "_views": 21 }, { "_id": "885b71ac-5a4b-4d2e-9770-a4a6f20425e9", "title": "15 Tips On How To Brand Yourself Online", "_views": 11 }, { "_id": "ca0e0d37-600b-42f7-a013-9b0f8e18f127", "title": "The Evolution Of Jamstack", "_views": 9 }, { "_id": "f4b65d11-c2e2-4320-a363-e7d420d03ed2", "title": "Modeling A GraphQL API For Your Blog", "_views": 5 } ] } } } ``` ### Filtering the result The previous example included all items for retrieving the most popular items. But often, you want to make the result even more accurate. For example, only show items published recently or in a particular category. So let's see how you can do that. - Expand the query with the following parameters: ```graphql copy query { Popular_Articles( where: { _publish_on_gt: "2021-01-01T00:00:00+00:00" categories: { _slug_any: [ "ux-design", "development" ] } }, limit: 3 ) { items { _id title _views } } } ``` - We added `_publish_on_gt: "2021-01-01T00:00:00+00:00"` to show only articles published after January 1, 2021. Note the underscore at the beginning of the parameter. - We added categories: **`{ _slug_any: [ "ux-design", "development" ] }`** to limit the result to only articles in the “UX Design” or the “Development” categories. Note the square brackets because you’re dealing with an array. - We added `limit: 3` to limit the number of results to three items. Result: ```json copy { "data": { "Popular_Articles": { "items": [ { "_id": "dd42b8c4-4773-4fdd-a71f-87e57b35beaa", "title": "Building User Trust In UX Design", "_views": 59 }, { "_id": "79c453ed-ffe2-4aec-8fb2-f549e3775264", "title": "Building A Video Streaming App With Nuxt.js", "_views": 49 }, { "_id": "5a53b271-898e-4eef-b877-5863b34b6ff9", "title": "UI Design Testing Tools I Use All The Time", "_views": 40 } ] } } } ``` ## Want to learn more? Check out the following chapters: - [A/B testing](https://docs.prepr.io/ab-testing/setting-up-ab-testing) - [Personalization](https://docs.prepr.io/personalization/setting-up-personalization) ## Schedule a free consultation Do you want to get started with recommendations but still have questions or want a demo? [Schedule a free call](https://prepr.io/get-a-demo) with a Prepr solution engineer. Source: https://docs.prepr.io/recommendations --- # Integrations Extend Prepr CMS with one of the standard integrations listed below. If you need to build a custom integration, check out the [creating a custom remote source](https://docs.prepr.io/content-modeling/creating-a-custom-remote-source) or [using webhooks](https://docs.prepr.io/development/best-practices/webhooks) resources instead. > **Note:** > > Help shape Prepr by sharing your Prepr development experience, asking questions and learning from other developers in our [developer roundtable](https://prepr.io/resources/developer-roundtable). - [ActiveCampaign](https://docs.prepr.io/integrations/activecampaign): Activate to add signup forms that feed contacts directly into your marketing automations. - [Algolia](https://docs.prepr.io/integrations/algolia): Index Prepr content with the performant Algolia search engine. - [BigCommerce](https://docs.prepr.io/integrations/bigcommerce): Add BigCommerce products directly in content items. - [Bynder](https://docs.prepr.io/integrations/bynder): Access enterprise DAM assets from Bynder directly in Prepr content items. - [Cloudinary](https://docs.prepr.io/integrations/cloudinary): Activate to select media from Cloudinary in content items. - [Commerce Layer](https://docs.prepr.io/integrations/commerce-layer): Add Commerce Layer catalog data directly in content items. - [Commercetools](https://docs.prepr.io/integrations/commercetools): Add Commercetools catalog data directly in content items. - [Customer.io](https://docs.prepr.io/integrations/customerio): Activate to sync external segments to Prepr. - [Form.io](https://docs.prepr.io/integrations/formio): Connect dynamic form data and APIs seamlessly to your interactive content. - [Formstack](https://docs.prepr.io/integrations/formstack): Automate form submissions and streamline workflows right from your content. - [Frontify](https://docs.prepr.io/integrations/frontify): Access Frontify brand assets directly in Prepr content items. - [Google Workspace](https://docs.prepr.io/integrations/google-workspace): Enable single sign-on with Google Workspace for Prepr users. - [HubSpot](https://docs.prepr.io/integrations/hubspot): Activate to personalize content using HubSpot lists or to include HubSpot forms in content items. - [Jotform](https://docs.prepr.io/integrations/jotform): Activate to embed interactive forms and collect responses directly in your content. - [Leadfeeder](https://docs.prepr.io/integrations/leadfeeder): Activate to personalize content using Leadfeeder company profiles. - [Leadinfo](https://docs.prepr.io/integrations/leadinfo): Activate to personalize content using Leadinfo matched companies. - [Mailchimp](https://docs.prepr.io/integrations/mailchimp): Embed signup forms to grow your audience and capture leads within your content. - [Mimir](https://docs.prepr.io/integrations/mimir): Access Mimir DAM assets directly in Prepr content items. - [Microsoft Entra ID (Azure)](https://docs.prepr.io/integrations/azure): Enable the Microsoft Entra ID single sign-on for Prepr users. - [OneLogin](https://docs.prepr.io/integrations/onelogin): Enable single sign-on from OneLogin for Prepr users. - [OpenID Connect](https://docs.prepr.io/integrations/openid): Enable single sign-on from your OpenID Connect identity provider for Prepr users. - [Pipedrive](https://docs.prepr.io/integrations/pipedrive): Activate to embed Pipedrive forms in content items. - [Prepr image processing](https://docs.prepr.io/integrations/image-processing): Automatically populate alt text and other image text values using AI or Exif data. - [Propeller](https://docs.prepr.io/integrations/propeller): Activate to include Propeller products directly in content items. - [ProspectPro](https://docs.prepr.io/integrations/prospectpro): Activate to personalize content using ProspectPro company profiles. - [SAML 2.0](https://docs.prepr.io/integrations/saml): Enable single sign-on from your SAML 2.0 identity provider for Prepr users. - [Salesforce](https://docs.prepr.io/integrations/salesforce): Activate to sync external segments to Prepr. - [Shopify](https://docs.prepr.io/integrations/shopify): Activate to select Shopify products directly in content items. - [Snitcher](https://docs.prepr.io/integrations/snitcher): Activate to personalize content using Snitcher company profiles. - [SuperOffice](https://docs.prepr.io/integrations/superoffice): Activate to personalize content using SuperOffice selections or to include SuperOffice forms in content items. - [Twilio Segment](https://docs.prepr.io/integrations/segment): Activate to sync external segments to Prepr. - [Typeform](https://docs.prepr.io/integrations/typeform): Embed Typeform templates to implement forms in your web app. - [Typesense](https://docs.prepr.io/integrations/typesense): Enable an optimized search on content items with Typesense. - [Vercel](https://docs.prepr.io/integrations/vercel): Allow users to deploy to Vercel directly from Prepr. - [Zapier](https://docs.prepr.io/integrations/zapier): Integrate any external system to tag a known visitor or trigger a Prepr event. Source: https://docs.prepr.io/integrations --- # AI-friendly documentation *From this guide, you'll learn how to use Prepr's machine-readable documentation formats with AI coding assistants for faster, more accurate development.* > **Note:** > > Help shape Prepr by sharing your Prepr development experience, asking questions and learning from other developers in our [developer roundtable](https://prepr.io/resources/developer-roundtable). ## Introduction Prepr's documentation is available in LLM-friendly formats that make it easy to integrate with AI coding assistants like Claude, ChatGPT, Cursor, and GitHub Copilot. These formats follow the [llms.txt](https://llmstxt.org/) standard, providing machine-readable documentation that AI assistants can use as context when helping you build with Prepr. By adding Prepr documentation to your AI assistant, you'll get: - Accurate answers based on the latest Prepr documentation - Code suggestions that follow Prepr best practices - Faster development with context-aware assistance - Reduced errors from outdated or incorrect information ## Available formats Prepr documentation is available in three LLM-friendly formats: ### llms.txt The `llms.txt` file provides an index of all documentation pages with titles and descriptions. **URL:** [https://docs.prepr.io/llms.txt](https://docs.prepr.io/llms.txt) **Best for:** Quick reference and helping AI assistants find relevant documentation sections. ### llms-full.txt The `llms-full.txt` file contains the complete Prepr documentation in a single, machine-readable text file. **URL:** [https://docs.prepr.io/llms-full.txt](https://docs.prepr.io/llms-full.txt) **Best for:** Providing full context to AI assistants for comprehensive answers about Prepr. ### Markdown format (.md) Any documentation page can be accessed in markdown format by appending `.md` to the URL. **Example:** [https://docs.prepr.io/graphql-api.md](https://docs.prepr.io/graphql-api.md) **Best for:** Getting the raw markdown content of individual pages for focused context. ## Using with AI ### Claude Projects [Claude Projects](https://claude.ai/) allow you to add documentation as project knowledge, giving Claude context for all conversations within that project. 1. Open [Claude.ai](https://claude.ai/) and create a new Project or open an existing one. 2. In the project settings, navigate to the **Files** section. 3. Click **+** and choose one of these options: - **Add file:** Download https://docs.prepr.io/llms-full.txt to your computer and upload the file - **Add text content:** Paste a link to https://docs.prepr.io/llms-full.txt in the text field 4. Claude will process and index the documentation. Now Claude can reference Prepr documentation when answering your questions about building with Prepr CMS. > **Note:** > > You can also add specific documentation pages by using the `.md` format, for example: https://docs.prepr.io/graphql-api/get-started.md ### ChatGPT There are two ways to use Prepr documentation with ChatGPT: **Option 1: Create a Project** 1. Go to [ChatGPT](https://chat.openai.com/) and create a new Project. 2. In the project settings, find the **Project files** section. 3. Download https://docs.prepr.io/llms-full.txt to your computer. 4. Upload the downloaded file to the project. All chats within this project will have access to the Prepr documentation. **Option 2: Build a Custom GPT** [Custom GPTs](https://help.openai.com/en/articles/8554397-creating-a-gpt) let you create specialized assistants with tailored knowledge. 1. Navigate to the [GPT Builder](https://chat.openai.com/gpts/editor). 2. Click **Create a GPT**. 3. Switch to the **Configure** tab and locate the **Knowledge** section. 4. Download https://docs.prepr.io/llms-full.txt and upload the file. 5. Add instructions such as: "You are a Prepr CMS specialist. Use the provided documentation to answer questions about headless CMS development, content modeling, and GraphQL API usage. Provide code examples when applicable." > **Note:** > > Custom GPTs and Projects require a ChatGPT Plus or Team subscription. Free users can paste relevant documentation sections directly into their conversations. ### Cursor [Cursor](https://cursor.sh/) is an AI-first code editor that can reference documentation directly. 1. Open a chat in Cursor. 2. Type `@Docs` in the chat input. 3. Scroll down and click **Add new doc**. 4. Paste the URL: https://docs.prepr.io/llms.txt 5. Set docs name to Prepr Now you can reference Prepr documentation in your prompts using `@Prepr` or by typing `@` and selecting Prepr from the documentation sources. ```plaintext Example prompt: @Prepr How do I fetch a single content item by slug using the GraphQL API? ``` ## Tips for using AI assistants with Prepr > **Note:** > > **Stay up to date:** We update Prepr documentation continuously with new features and improvements. Make sure to refresh the documentation periodically in your AI assistant if you're manually downloading the LLMs. > > **Combine with code context:** AI assistants work best when they have both documentation and your project's code context. Share relevant code snippets along with your questions for more accurate suggestions. > > **Verify critical information:** While AI assistants are helpful, always verify critical implementation details like API endpoints, authentication methods, and data schemas against the official documentation. > **Note:** > > If you have any questions about using Prepr with AI assistants, please [contact our Support team](https://prepr.io/support). Source: https://docs.prepr.io/ai-friendly-docs --- # AI agent-ready best practices for front-end applications *This guide gives you some tips on how to build a front-end application that AI crawlers, RAG (Retrieval-Augmented Generation) agents, and LLM search engines can easily fetch, chunk, and parse.* ## Introduction AI-powered search engines, chat assistants, and retrieval agents increasingly fetch web pages directly to answer questions, compare products, and cite sources. If your front end is hard to crawl, slow to render, or difficult to parse, those systems may skip your content entirely or extract it poorly. Being AI agent-ready means serving clean, accessible, machine-readable pages from the first request. Your content should be available without unnecessary session barriers, easy to interpret through semantic HTML and structured data, and fast enough for bots that operate with short timeouts. This guide focuses on the front-end implementation patterns that help AI agents access and understand your site reliably. This work supports Generative Engine Optimization (GEO), which focuses on making your content easier for AI systems to retrieve, interpret, and cite in generated answers. In practice, AI agent readiness is the technical foundation for GEO: if agents cannot fetch and parse your front end reliably, your content is much less likely to surface in AI-powered experiences. > **Note:** > > For content strategy and editorial best practices to improve GEO, check out the [How to improve your visibility in AI search](https://prepr.io/blog/how-to-improve-your-visibility-in-ai-search-a-practical-geo-guide-for-content-marketers) blog post. ## Infrastructure security (Preventing false positives) Web Application Firewalls (WAFs) and rate-limiters are engineered to mitigate DDoS attacks and malicious scrapers. However, default security rules frequently group legitimate AI agents into generic "Bad Bot" or "Unknown Scraper" categories, blocking them entirely. ### WAF customization Configure your WAF to allow verified AI bots, not just requests that claim a bot-like `User-Agent`. Treat the header as a hint, then verify it against published bot IP ranges, reverse DNS rules, or your provider's bot verification program. Instead of hard-blocking high-frequency automated traffic from these sources, move them to a Managed Challenge (like Cloudflare Turnstile) rather than an interactive text/image CAPTCHA, which headless AI parsers cannot bypass. ### Verify the bot `GPTBot`, `OAI-SearchBot`, `ChatGPT-User`, `ClaudeBot`, `Claude-Web`, `claude-user`, `PerplexityBot`, and `Google-Extended` are useful detection hints, but they are not identity proofs. At a minimum: - Match the request `User-Agent` against an explicit allowlist. - Verify that the request originates from a published vendor IP range or a provider-level verified-bot program. - Keep a separate fallback policy for unknown automation so spoofed browser traffic does not inherit the same bypass. This matters because a middleware bypass that trusts the `User-Agent` string alone can be trivially spoofed. ### Explicit robots.txt configuration Ensure your root directory explicitly grants access to generative engines. Traditional Googlebot handles indexation for standard search, but specific tokens dictate LLM training and RAG ingestion. Configure your robots.txt as follows: ```plaintext filename="robots.txt" copy User-agent: GPTBot Allow: / User-agent: OAI-SearchBot Allow: / User-agent: ClaudeBot Allow: / User-agent: Claude-Web Allow: / User-agent: claude-user Allow: / User-agent: PerplexityBot Allow: / User-agent: Google-Extended Allow: / # Secure internal API routes and admin panels from scrapers Disallow: /api/private/ Disallow: /admin/ ``` ## State-free middleware Modern front-end applications frequently use middleware layer checks to detect cookies, manage geolocation routing, or enforce cookie-consent scripts before rendering page layouts. However, AI agents usually operate with little or no durable browser state. Training crawlers rarely preserve state, and even in-product browse tools that carry user context still frequently fetch pages without your expected session cookies. If your front-end requires a valid session or cookie acknowledgment to return content, the bot will scrape a blank page or get caught in a redirect loop. To fix this, write an exception rule directly into your edge/server middleware (such as the Next.js `middleware.ts`) that detects verified AI user-agents and bypasses client-side session or location checks. Deliver your content in Prepr transparently on the initial anonymous HTTP GET request. For example, in Next.js: ```ts filename="middleware.ts" copy const AI_BOT_UA = /GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-Web|claude-user|PerplexityBot|Google-Extended/i function isVerifiedAIBot(request: NextRequest) { const userAgent = request.headers.get('user-agent') ?? '' if (!AI_BOT_UA.test(userAgent)) { return false } // Pair the UA allowlist with IP verification, reverse DNS validation, // or your WAF provider's verified-bot signal before bypassing auth logic. return request.headers.get('x-bot-verified') === '1' } if (isVerifiedAIBot(request)) { return NextResponse.next() } // Existing geolocation, consent, or session enforcement goes here. return NextResponse.next() } ``` Keep the allowlist small and explicit, and document that the strings can change over time. For OpenAI specifically, `ChatGPT-User` is user-triggered retrieval traffic rather than an autonomous crawler, so manage it in middleware or WAF policy rather than assuming `robots.txt` alone will control it. ## Optimize the ingestion layer (llms.txt) Traditional search engines and AI agents use `sitemap.xml` for discovery. For AI agents, you can complement the sitemap by giving them a condensed, machine-friendly summary of your most important content in one place. Implement this with an `llms.txt` (and a more comprehensive llms-full.txt) at the root of your front-end repository (https://yourdomain.com/llms.txt). An `llms.txt` file is plain Markdown. A practical structure is: - An H1 with the site or product name - A short blockquote that explains what the site contains - Section headings for major content areas - One link per page with a short description For example: ```md filename="llms.txt" copy # Prepr Documentation > Complete documentation for Prepr CMS - a headless CMS for building personalized digital experiences. ## Pages - [Prepr CMS Documentation - Headless CMS for modern applications](https://docs.prepr.io/index): Learn how to build fast, scalable applications with Prepr CMS. Complete guides for Next.js, Nuxt, Laravel, and more. Get started with our headless CMS today. - [Quick Start Guide - Get Started with Prepr CMS in 15 Minutes](https://docs.prepr.io/quick-start-guide): Learn how to set up Prepr CMS, create content models, add content, and retrieve it using the GraphQL API. Perfect for developers new to headless CMS. - [Changelog - Latest Updates and Features | Prepr CMS](https://docs.prepr.io/changelog): Stay up to date with the latest Prepr CMS features, improvements, and bug fixes. See what's new in our headless CMS platform. - [Prepr CMS product roadmap - Upcoming features and improvements](https://docs.prepr.io/roadmap): Explore Prepr's product roadmap to see upcoming features, integrations, and improvements. Learn what we're building for the future of headless CMS. - [Setting up your production-ready Prepr project](https://docs.prepr.io/project-setup): Learn how to set up your Prepr project for production with our comprehensive guide and essential resources. - [Content modeling in Prepr CMS](https://docs.prepr.io/content-modeling): Learn how to set up schemas and models effectively in Prepr CMS for organized content management. - [Connecting a front-end framework with Prepr CMS](https://docs.prepr.io/connecting-a-front-end-framework): Learn how to connect your favorite front-end frameworks to Prepr CMS for personalized content delivery. - [Developing with Prepr CMS: guides and best practices](https://docs.prepr.io/development): Learn how to develop with Prepr CMS, including front-end frameworks, CI/CD, and integrations for a smooth development experience. - [Guide to content management in Prepr](https://docs.prepr.io/content-management): Learn how to manage content, assets, localization, and collaboration in Prepr CMS effectively. - [Data collection guides for Prepr CMS](https://docs.prepr.io/data-collection): Learn about data collection features in Prepr and how to set up tracking, recording, and managing visitor data effectively. ... ``` For Prepr projects, this file does not need to be static. You can generate it dynamically from Prepr content: - Generate `llms.txt` from Prepr content in a route handler by querying published pages through the GraphQL API. - Fetch a curated list of published docs pages from the Prepr GraphQL API in a route handler or edge function. - Use editorial metadata such as title, summary, category, and slug to build the Markdown structure. - Regenerate the output when content changes if needed, for example, by using Prepr webhooks to revalidate static pages. ## Zero-latency renders and Edge caching AI scrapers are heavily optimized for processing speed; they will not wait for slow backend database operations or sluggish API responses. Most AI crawlers enforce strict internal connection timeout limits (frequently between 2 to 5 seconds). ### Server-rendered and prerendered pages Never rely on heavy client-side JavaScript fetching that serves a loading spinner for several seconds. The page source must contain the full text content immediately upon the initial server response. ### Time to First Byte (TTFB) mitigation Move your data fetching closer to the bot. Use server-side or CDN caching to avoid repeated origin work for the same content. Cache completely rendered HTML pages at the CDN edge so that when an AI bot hits a content URL, the response time is safely under 200ms. Also keep canonical URLs direct and stable. Avoid redirect chains longer than one 301 hop, and make sure each document resolves to a single canonical URL so retrieval systems do not split citations across duplicate paths. ## Enforce rigid semantic HTML When an LLM scraper successfully fetches your page, it strips out styling and looks strictly at the document hierarchy to define context and determine which answers map to specific headings. In your layout, map your content fields to explicit, native HTML semantic elements. Never use styled `
` or `` tags to replace headings. Ensure `

`, `

`, and `

` tags follow a sequential hierarchy. Isolate definitions, feature callouts, or summary takeaways using structural semantic tags like `

``` Now when you click a blog post, you see a detailed post page like in the image below. ![Blog post detail page](https://assets-site.prepr.io/lgu11mftdw6//laravel-blog-post-detail-page.png) ## All done Congratulations! You have successfully connected a Laravel project to Prepr for a simple Blog app. ## Next steps To learn more on how to expand your project, check out the following resources: - [Add styling and Prepr personalization to your Laravel app](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide) - [Draft more queries for components and other field types](https://docs.prepr.io/graphql-api) - [Check out the Laravel GraphQL SDK repo](https://github.com/preprio/laravel-graphql-sdk) - [Deploy your Laravel app](https://devcenter.heroku.com/articles/getting-started-with-laravel#deploying-to-heroku) Source: https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-quick-start-guide --- # Complete guide to Laravel and Prepr *This guide shows you how to connect Prepr to a Laravel project including styling, adaptive content, and A/B testing.* Follow the steps below in the recommended order to set up your own working Laravel website with adaptive content, A/B testing and a preview bar. You can also customize this project to fit the requirements for your web app. - [Introduction](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/introduction): Get a preview of the website you'll build and what you need to get started. - [Set up a Laravel project](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-1-set-up-a-laravel-project): Install and create a working Laravel project with static webpages. - [Make it dynamic](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-2-make-the-project-dynamic): Connect to Prepr using Apollo Client to retrieve and show Acme Lease content. - [Set up data collection](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-3-set-up-data-collection): Set up data collection for A/B testing and personalization. - [Add A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-4-add-ab-testing): Set up and see how an A/B test runs in your website. - [Add personalization](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-5-add-personalization): Deliver a personalized experience to the visitor. Source: https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide --- # Laravel + Prepr CMS Need info on Prepr for a Laravel app? Look no further. This all-in-one page gives you all the resources you need to connect your project to Prepr and to set up personalized pages exactly the way your marketers want them. ## Laravel Quick start guide - [Follow the guide](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-quick-start-guide): The quick start guide covers the basics of connecting Prepr CMS with Laravel to make a basic blog in less than 10 minutes. - [Visit the GitHub repo](https://github.com/preprio/laravel-quick-start): Check out the source code and clone the repo for a quick Laravel project set up. ## Laravel Complete guide - [Follow the guide](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide): The complete guide to Laravel and Prepr demonstrates how to create compelling web pages in Prepr, including adaptive content, A/B testing and a preview bar. - [Visit the GitHub repo](https://github.com/preprio/laravel-complete-starter): Check out the source code and clone the repo for a complete Laravel project set up. ## Laravel SDKs - [Laravel GraphQL Provider](https://docs.prepr.io/laravel/laravel-graphql-provider): Visit the GitHub repository for the GraphQL provider. - [Laravel REST API Provider](https://docs.prepr.io/laravel/laravel-rest-provider): Visit the GitHub repository for the REST provider. ## Other resources - [How to set up personalization](https://docs.prepr.io/personalization/setting-up-personalization) - [How to measure A/B testing](https://docs.prepr.io/ab-testing/setting-up-ab-testing) Source: https://docs.prepr.io/connecting-a-front-end-framework/laravel --- # React + Prepr CMS This overview page gives you the resources you need to connect your React project to Prepr. > **Note:** > > If you want to build a new web app fully with React, they recommend using a React-powered framework instead. > The `create-react-app` package is no longer being maintained. Check out the [React docs](https://react.dev/learn/start-a-new-react-project) for more details. Also check out our [Next Quick start guide](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-quick-start-guide) to create a new project with the most popular React framework. ## Other resources - [Rendering strategies, SSR or SSG?](https://docs.prepr.io/development/best-practices/csr-ssr-ssg) - [How to set up personalization](https://docs.prepr.io/personalization/setting-up-personalization) - [How to measure A/B testing](https://docs.prepr.io/ab-testing/setting-up-ab-testing) Source: https://docs.prepr.io/connecting-a-front-end-framework/react --- # Vue Quick start guide *Estimated duration: 10 minutes* *This guide shows you how to connect Prepr to a Vue project to get data from Prepr CMS. You'll learn how to make a simple blog with Vue and Prepr CMS. By the end of this guide, you'll have a working app that looks like the image below.* ![Blog site end result](https://assets-site.prepr.io/6eqxbjrigrdt//vuejs-blog-posts-result.png) > **Note:** > > If you want to skip ahead, try out [the zero installation demo on Stackblitz](https://stackblitz.com/~/github.com/preprio/vue-quick-start) or clone the [repository on GitHub](https://github.com/preprio/vue-quick-start) to run the demo locally. ## Prerequisites You need to have the following setup before you connect your Vue project to Prepr. - [A free Prepr account](https://signup.prepr.io) - [An environment with demo data in Prepr](https://docs.prepr.io/project-setup/setting-up-environments#create-an-environment) - [The latest version of Node.js](https://nodejs.org/en) ## Create a simple Blog website with Vue.js and Prepr CMS ### Create a new Vue project The instructions below will guide you on how to create a new Vue project for your blog app. If you have an existing Vue project then you can skip this step. 1. Open a terminal and execute the command below to create a new Vue project called `prepr-vue`. ```bash copy npm create vue@latest prepr-vue ``` 2. Choose the following options to match the structure of the project in this guide. - `No` for Typescript - `Router (SPA development)` (go to this option and press space to enable it) - `No` to `Skip all example code and start with a blank Vue project?` 3. When the project is successfully created, go to the `prepr-vue` folder, the root directory of the project, and start the project with the following commands in the terminal: ```bash copy cd prepr-vue ``` ```bash copy npm install ``` ```bash copy npm run dev ``` 4. You should now be able to view your app on your localhost, for example, `http://localhost:5173/`. 5. Open your Vue project with your preferred code editor. 6. Go to the `src/views` folder and replace the code in the `Homeview.vue` file with the following code to display a single heading: ```html filename="./src/views/Homeview.vue" copy ``` 7. Replace the code in the `App.vue` file in the `src` folder with the code below to remove the Vue welcome and formatting from the page. ```vue filename="./src/App.vue" copy ``` 8. This project doesn't include any styling, so go to the `src/assets` folder and empty the `main.css` file to remove the existing styling. You should now see something like the image below on your localhost. ![Vue.js empty blog site](https://assets-site.prepr.io/7hg3c8fmxb7r//vujs-empty-blog-site.png) ### Install Apollo Client Apollo Client is an integration tool that helps to retrieve CMS data with GraphQL. The instructions below show you how to install [Apollo Client](https://apollo.vuejs.org/guide/installation.html) so that you can execute GraphQL queries to request data from the Prepr API. 1. Stop the server you started in the above step (`CTRL-C`) and run the commands below in the terminal to install Apollo Client to execute GraphQL queries in a Vue project. ```bash copy npm install --save graphql graphql-tag @apollo/client ``` 2. Create a folder called `services` in the `src` folder. Then, create a file called `apollo-client.js` in this folder. Copy the following code to this file to import Apollo Client: ```js filename="./src/services/apollo-client.js" copy import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client/core' // HTTP connection to the API const httpLink = createHttpLink({ // You should use an absolute URL here uri: `https://graphql.prepr.io/${import.meta.env.VITE_PREPR_ACCESS_TOKEN}`, }) // Cache implementation const cache = new InMemoryCache() // Create the apollo client const apolloClient = new ApolloClient({ link: httpLink, cache, }) export default apolloClient ``` 3. We recommend using environment variables to store sensitive information like access tokens. To add environment variables, create a `.env` file in the root directory of your project and add the access token like this: ```bash filename="./.env" copy VITE_PREPR_ACCESS_TOKEN= ``` 4. Replace the placeholder value `` with an access token from Prepr. Get an access token by logging into your Prepr account:\ a. Click the icon and choose the **Access tokens** option to view all the access tokens.\ b. Copy the *GraphQL Production* access token to only retrieve published content items on your site. ![GraphQL production access token example](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) > **Note:** > > Use the *GraphQL Production* access token to request published content items for your live app and use the *GraphQL Preview* token to make a [preview of unpublished content items](https://docs.prepr.io/project-setup/setting-up-previews-and-visual-editing) for your content editors. 5. Execute the following command to make sure that Apollo Client is installed correctly: ```bash copy npm run dev ``` If your app runs without errors, then the setup above was done correctly. The next step is to fetch content from Prepr using the installed Apollo Client. ### Add a GraphQL query Now that Apollo Client is installed and connected to Prepr, fetch the blog posts by adding a GraphQL query. 1. Go to the `src` folder and create a `queries` directory. In here, create a file named `get-posts.js`. 2. Add the following query to this file to retrieve all blog posts: ```js filename="./src/queries/get-posts.js" copy import gql from 'graphql-tag' export const GetPosts = gql` query { Posts { items { _id _slug title } } } ` ``` > **Note:** > > You can create and [test GraphQL queries](https://docs.prepr.io/graphql-api/test-queries) using the [Apollo explorer](https://studio.apollographql.com/sandbox/explorer) from Prepr. Open the **API Explorer** from the *Post* content item page in Prepr or the access token page. If you’re using preloaded demo data in your Prepr CMS environment as mentioned above in the [Prerequisites](#prerequisites) section, you should have a few published blog posts as shown in the below image. The query will retrieve the *ID*, *Slug*, and *Title* of each blog post. ![demo blog posts](https://assets-site.prepr.io/6ayuw6ta7ccw//content-list-page-filtered-on-posts-for-qsgs.png) In the next step, we'll fetch and process the query response. ### Fetch multiple blog posts Now that the query has been added, fetch the blog posts from Prepr and display them in the app. 1. Open the `HomeView.vue` file and add a script and HTML to execute the query and display the query results with the code below. ```vue filename="./src/views/HomeView.vue" copy ``` Now when you view the website on your localhost, you'll see something like the image below. ![Blog site end result](https://assets-site.prepr.io/6eqxbjrigrdt//vuejs-blog-posts-result.png) ### Create dynamic routing Now you have the list of blog posts with links to them. When a visitor clicks a blog post link, your app should open a detailed post page automatically. However, we haven't created the detailed page yet. Now, when you click the link, an empty page opens with the slug in the URL. The instructions below show you how to create a route from the main page to the detailed page. 1. We added the Vue router as part of the installation, so adding routing is simple. Just update the `index.js` file in the `router` folder to add a route to the detailed blog post page as follows: ```js filename="./src/router/index.js" copy {4-5,16-21} import { createRouter, createWebHistory } from 'vue-router' import HomeView from '@/views/HomeView.vue' // import the detailed blog post page import PostView from "@/views/PostView.vue"; const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', name: 'home', component: HomeView }, // Include routing to the detailed blog post page with the slug from the URL { path: '/blog/:slug', name: 'post', component: PostView } ] }) export default router ``` Continue with the next step to fetch the post details and display the page. ### Fetch individual blog posts Add another query to fetch a specific blog post by its slug and make this page visible when clicking the link for a post. 1. Create a file called `get-post-by-slug.js` in the `queries` folder and add the following query to this file to query a specific blog post by its slug: ```js filename="./src/queries/get-post-by-slug.js" copy import gql from 'graphql-tag' export const GetPostBySlug = gql` query ($slug: String) { Post(slug: $slug) { _id title cover { url(width: 300, height: 250) } content { __typename ... on Text { _id body text } ... on Assets { items { _id url(width: 300, height: 250) } } } } } ` ``` Now that the query is added, fetch the individual blog post by its slug. Fetch the title, cover image and the post content. > **Note:** > > The Blog post content is stored in a *Dynamic content field*. Check out the GraphQL docs for more details on [how to fetch the data within this field](https://docs.prepr.io/graphql-api/schema-field-types-dynamic-content-field). 2. Open the `views` folder and create a new file `PostView.vue` with the following code: ```vue filename="./src/views/PostView.vue" copy ``` Now, when you view your site, you can click any link to direct you to that specific blog post like in the image below. ![Blog post end result](https://assets-site.prepr.io/j6k2o7tdw2q//vuejs-blog-post-detail-page.png) ## All done Congratulations! You have successfully connected a Vue project to Prepr for a simple Blog app. ## Next steps To learn more on how to expand your project, check out the following resources: - [Draft more queries for components and other field types](https://docs.prepr.io/graphql-api) - [How to install and use Tailwind CSS](https://tailwindcss.com/docs/guides/vite) Source: https://docs.prepr.io/connecting-a-front-end-framework/vuejs/vue-quick-start-guide --- # Vue.js + Prepr CMS Need info on Prepr for a Vue app? Look no further. This all-in-one page gives you all the resources you need to connect your project to Prepr. > **Note:** > > Looking for our SDK? Check out the [Using an SDK article](https://docs.prepr.io/connecting-a-front-end-framework/using-an-sdk) on why you don't need one. ## Vue.js Quick start guide - [Follow the guide](https://docs.prepr.io/connecting-a-front-end-framework/vuejs/vue-quick-start-guide): The quick start guide covers the basics of connecting Prepr CMS with Vue to make a basic blog in less than 10 minutes. - [Visit the GitHub repo](https://github.com/preprio/vue-quick-start): Check out the source code and clone the repo for a quick Vue project set up. - [Try it yourself on Stackblitz](https://stackblitz.com/~/github.com/preprio/vue-quick-start): Check out the source code and play around to learn more. ## Other resources - [Rendering strategies, SSR or SSG?](https://docs.prepr.io/development/best-practices/csr-ssr-ssg) - [How to set up personalization](https://docs.prepr.io/personalization/setting-up-personalization) - [How to measure A/B testing](https://docs.prepr.io/ab-testing/setting-up-ab-testing) Source: https://docs.prepr.io/connecting-a-front-end-framework/vuejs --- # Angular Quick start guide *Estimated duration: 10 minutes* *This guide shows you how to connect Prepr to an Angular project to get data from Prepr CMS. You'll learn how to make a simple blog with Angular and Prepr CMS. By the end of this guide, you'll have a working app that looks like the image below.* ![Blog site end result](https://assets-site.prepr.io/fp1a9v4zuar//angular-blog-post-site-end-result.png) > **Note:** > > If you want to skip ahead, clone the [repository on GitHub](https://github.com/preprio/angular-quick-start) to run the demo locally. ## Prerequisites You need to have the following setup before you connect your Angular project to Prepr. - [A free Prepr account](https://signup.prepr.io) - [An environment with the demo content in Prepr](https://docs.prepr.io/project-setup/setting-up-environments#create-an-environment) - [The latest version of Node.js](https://nodejs.org/en) ## Create a simple Blog website with Angular and Prepr CMS ### Create a new Angular project The instructions below will guide you on how to create an empty Angular project for your blog app. If you have an existing Angular project, then you can skip this step. 1. Open a terminal, go to your development projects folder and execute the command below to install the latest *Angular CLI*: ```bash copy npm i -g @angular/cli@latest ``` 2. Now that the *Angular CLI* is installed you can create an Angular project called `prepr-angular`. Choose `Y` for each of the options. ```bash copy ng new prepr-angular ``` 3. When the project is successfully created, execute the commands below to go to the `prepr-angular` folder, the root directory of the project, and start the project. ```bash copy cd prepr-angular ``` ```bash copy ng serve --open ``` 4. A browser window opens automatically for you to view your app on your localhost, for example, `http://localhost:4200/`. ### Connect to Prepr Set up a connection to Prepr to retrieve CMS data with GraphQL. The instructions below show you how to connect to Prepr directly from your project so that you can execute GraphQL queries to request data from the Prepr API. 1. Stop the localhost website server (`CTRL-C`) if it's running and open your Angular project with your preferred code editor. 2. To store the Prepr endpoint value, create a new folder `environments` in the `src` directory and add the following code to a new file `environment.ts` in this folder. ```ts copy filename="./src/environments/environment.ts" export const environment = { PREPR_ENDPOINT: '' }; ``` 3. Replace the placeholder value `` with the *API URL* of an access token from Prepr. Get an access token by logging into your Prepr account:\ a. Click the icon and choose the **Access tokens** option to view all the access tokens.\ b. Click to open the *GraphQL Production* access token, and copy the *API URL* to only retrieve published content items on your site for now. ![GraphQL production access token example](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) > **Note:** > > Use the *GraphQL Production* access token to request published content items for your live app and use the *GraphQL Preview* token to make a [preview of unpublished content items](https://docs.prepr.io/project-setup/setting-up-previews-and-visual-editing) for your content editors. 4. To connect to Prepr GraphQL API, create a new `services` folder in the `app` directory and add the following code to a new file called `prepr.service.ts`: ```ts copy filename="./src/app/services/prepr.service.ts" import { Injectable } from '@angular/core'; import { environment } from '../../environments/environment'; @Injectable({ providedIn: 'root', }) export class PreprService { async fetchData(query: string, variables?: any): Promise { const response = await fetch(environment.PREPR_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, variables }), }); return response.json(); } } ``` 5. Execute the following command to restart the server. ```bash copy npm start ``` If your app runs without errors, then the setup above was done correctly. The next step is to fetch content from Prepr using the endpoint that you set up. ### Add a GraphQL query Now that your app is connected to Prepr, fetch the blog posts from Prepr by adding a GraphQL query. 1. Create a `queries` folder in the `app` folder and create a file named `get-posts.ts`. 2. Add the following query to this file to retrieve all blog posts: ```ts filename="./src/app/queries/get-posts.ts" copy export const GetPosts = ` query { Posts { items { _id _slug title } } } ` ``` > **Note:** > > You can create and [test GraphQL queries](https://docs.prepr.io/graphql-api/test-queries) using the [Apollo explorer](https://studio.apollographql.com/sandbox/explorer) from Prepr. Open the **API Explorer** from the *Post* content item page in Prepr or the access token page. If you’re using preloaded demo data in your Prepr CMS environment, as mentioned above in the [Prerequisites](#prerequisites) section, you should have a few published blog posts as shown in the below image. The query will retrieve the *ID*, *Slug*, and *Title* of each blog post. ![demo blog posts](https://assets-site.prepr.io/6ayuw6ta7ccw//content-list-page-filtered-on-posts-for-qsgs.png) In the next step, we'll fetch and process the query response. ### Fetch blog posts Now that the query has been added, fetch the blog posts from Prepr and display them in the app. You can do this by creating an overview page in the app, by following the instructions below. 1. First, generate the blog post overview component to contain the code to fetch and render the list of blog posts with this command: ```bash copy ng generate component post-overview ``` You'll see a new folder `post-overview` in the `app` folder with the generated files. 2. To run the query and fetch the list of posts, replace the generated code with the code below in the `post-overview.ts` file. ```ts filename="./src/app/post-overview/post-overview.ts" copy import { Component, effect, inject } from '@angular/core'; import { PreprService } from '../services/prepr.service'; import { GetPosts } from '../queries/get-posts'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; @Component({ selector: 'app-post-overview', standalone: true, imports: [CommonModule, RouterModule], templateUrl: './post-overview.html', styleUrls: ['./post-overview.css'] }) export class PostOverview { posts: any[] = []; private preprService = inject(PreprService); constructor() { effect(async () => { const response = await this.preprService.fetchData(GetPosts); this.posts = response.data.Posts.items; }); } } ``` 3. Clear out the existing HTML by replacing the code in the \`app.html' with the simple code below: ```ts copy filename="./src/app/app.html" ``` 4. To display the list of blog posts, add the following code to the `post-overview.html`. ```html filename="./src/app/post-overview/post-overview.html" copy

My blog site

``` 5. To include routing to the post overview component, add the following code to the `app.routes.ts` file: ```ts filename="./src/app/app.routes.ts" copy {2,4-9} import { Routes } from '@angular/router'; import {PostOverview} from './post-overview/post-overview'; export const routes: Routes = [ { path: '', component: PostOverview, } ]; ``` Now when you view the website on your localhost, you'll see something like the image below. ![blog posts](https://assets-site.prepr.io/fp1a9v4zuar//angular-blog-post-site-end-result.png) ### Fetch individual blog posts Now you have the list of blog posts with links to them. When a visitor clicks a blog post link, your app should open a detailed post page automatically. However, we haven't created the detailed page yet. Now, when you click the link, nothing changes in your website. The instructions below show you how to create a route from the main page to the detailed page and how to fetch the post details based on the slug of the post that was clicked. Add another query to fetch a specific blog post by its slug. 1. Create a file called `get-post-by-slug.ts` in the `queries` folder and add the following query to this file to query a specific post by its slug: ```ts filename="./src/app/queries/get-post-by-slug.ts" copy export const GetPostBySlug = ` query ($slug: String) { Post (slug: $slug) { _id title cover { url(width: 300, height: 250) } content { __typename ... on Text { _id body text } ... on Assets { items { _id url(width: 300, height: 250) } } } } } ` ``` Now that the query has been added, fetch the individual post details by its slug and display them in the app. You can do this by creating a blog post detail page in the app, by following the instructions below. 2. First, generate the blog post detail component to contain the code to fetch and render the post details with this command: ```bash copy ng generate component post-detail ``` You'll see a new folder `post-detail` with the generated files in the `app` folder. 3. To run the query and fetch the details of a specific post, replace the generated code with the code below in the `post-detail.ts` file. ```ts filename="./src/app/post-detail/post-detail.ts" copy import { Component, effect, inject } from '@angular/core'; import { PreprService } from '../services/prepr.service'; import { GetPostBySlug } from '../queries/get-post-by-slug'; import { ActivatedRoute } from '@angular/router'; import { CommonModule } from '@angular/common'; import { RouterModule } from '@angular/router'; @Component({ selector: 'app-post-detail', standalone: true, imports: [CommonModule, RouterModule], templateUrl: './post-detail.html', styleUrl: './post-detail.css' }) export class PostDetail { post: any = {}; private route = inject(ActivatedRoute); private preprService = inject(PreprService); constructor() { effect(async () => { const slug = `blog/${this.route.snapshot.paramMap.get('slug')}`; const response = await this.preprService.fetchData(GetPostBySlug, { slug }); this.post = response.data.Post; }); } } ``` > **Note:** > > The blog post content is stored in a *Dynamic content field*. Check out the GraphQL docs for more details on [how to fetch the data within this field](https://docs.prepr.io/graphql-api/schema-field-types-dynamic-content-field). 4. To display the blog post details, add the below code to the `post-detail.html`. ```html filename="./src/app/post-detail/post-detail.html" copy

{{ post.title }}

@for (contentType of post.content; track contentType) { @if (contentType.__typename === "Assets" && contentType.items.length) { } @if (contentType.__typename === "Text") {
} }
``` 5. To include routing to the post detail component, add the following code to the `app.routes.ts` file: ```ts filename="./src/app/app.routes.ts" copy {3,9-13} import { Routes } from '@angular/router'; import {PostOverview} from './post-overview/post-overview'; import {PostDetail} from './post-detail/post-detail'; export const routes: Routes = [ { path: '', component: PostOverview, }, { path: 'blog/:slug', component: PostDetail, }, ]; ``` Now, when you view your site, you can click blog post to open the specific post like in the image below. ![Blog post detail page](https://assets-site.prepr.io/4t0tmpgfml9f//angular-blog-page-detail.png) ## All done Congratulations! You have successfully connected an Angular project to Prepr for a simple Blog app. ## Next steps To learn more on how to expand your project, check out the following resources: - [Draft more queries for components and other field types](https://docs.prepr.io/graphql-api) - [How to install and use Tailwind](https://tailwindcss.com/docs/installation/framework-guides/angular) - [Deploy your Angular app with Vercel](https://vercel.com/guides/deploying-angular-with-vercel) Source: https://docs.prepr.io/connecting-a-front-end-framework/angular/angular-quick-start-guide --- # Angular + Prepr CMS Need info on Prepr for an Angular app? Look no further. This all-in-one page gives you all the resources you need to connect your project to Prepr. ## Angular Quick start guide - [Follow the guide](https://docs.prepr.io/connecting-a-front-end-framework/angular/angular-quick-start-guide): The quick start guide covers the basics of connecting Prepr CMS with Angular to make a basic blog in less than 10 minutes. - [Visit the GitHub repo](https://github.com/preprio/angular-quick-start): Check out the source code and clone the repo for a quick Astro project set up. ## Other resources - [Rendering strategies, SSR or SSG?](https://docs.prepr.io/development/best-practices/csr-ssr-ssg) - [How to set up personalization](https://docs.prepr.io/personalization/setting-up-personalization) - [How to measure A/B testing](https://docs.prepr.io/ab-testing/setting-up-ab-testing) Source: https://docs.prepr.io/connecting-a-front-end-framework/angular --- # PHP Quick start guide *Estimated duration: 10 minutes* *This guide shows you how to connect Prepr to a PHP project to get data from Prepr CMS. You’ll learn how to build a simple blog with PHP and Prepr CMS. When you reach the end of this guide, you'll have a working app that looks something like the image below.* ![blog site end result](https://assets-site.prepr.io/1h1i3di3fxkj//php-blog-site-post-list.png) > **Note:** > > If you want to skip ahead, clone the [repository on GitHub](https://github.com/preprio/php-quick-start) to run the demo locally. ## Prerequisites You need to have the following setup before you connect your PHP project to Prepr. - [A free Prepr account](https://signup.prepr.io) - [An environment with demo data in Prepr](https://docs.prepr.io/project-setup/setting-up-environments#create-an-environment) ## Create a simple Blog website with PHP and Prepr CMS ### Create a new PHP project Before creating your first PHP project, you should ensure that your local machine has PHP and [Composer](https://getcomposer.org/) installed. If you are developing on macOS, PHP and Composer can be installed via [Homebrew](https://brew.sh/). This guide is based on the 8.2 PHP version. The instructions below will guide you on how to create an empty PHP project for your blog app. If you have an existing PHP project then you can skip this step. 1. Create a new folder called `prepr-php`. Open this new project in your preferred code editor and create a new folder called `public`. In this folder create a new file called `index.php`. 2. Add the following code to the `index.php`: ```php filename="./public/index.php" copy '; echo '

My blog site

'; echo ''; ``` 3. Run the following command in the terminal to start the server: ```bash copy php -S localhost:8000 ``` 4. You should now be able to view your app in your browser: ```bash copy http://localhost:8000/public/index.php ``` ### Install the Prepr GraphQL SDK The Prepr GraphQL SDK is an integration tool that helps to retrieve CMS data with GraphQL. The instructions below show you how to install the Prepr GraphQL SDK so that you can add GraphQL queries to request data from the Prepr API. 1. Execute the following command in the terminal: ```bash copy composer require preprio/php-graphql-sdk ``` 2. Next, you need to get the URL of the endpoint. This URL includes the access token for the environment you want to query from. Get this URL by logging into your Prepr account:\ a. Click the and choose the **Access tokens** option to view all the access tokens.\ b. Copy the *API URL* value from the *GraphQL Production* access token to only retrieve published content items. ![GraphQL production access token example](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) > **Note:** > > Use the *GraphQL Production* API URL to request published content items for your live app and use the *GraphQL Preview* value to make a preview of unpublished content items for your content editors. 3. Update the `index.php` file in `public` folder with the following code and replace the `` placeholder with the *API URL* that you copied in the previous step to connect to Prepr: ```php filename="./public/index.php" copy '); echo '
'; echo '

My blog site

'; echo '
'; ``` 4. Refresh the page in your browser. If the app runs without errors, then the setup above was done correctly. The next step is to fetch content from Prepr using the installed GraphQL SDK. ### Add a GraphQL query Now that your Prepr GraphQL SDK is installed and connected to Prepr, fetch the blog posts by adding a GraphQL query. 1. Create a `queries` directory in the root directory of your project and create a file named `get-posts.graphql`. 2. Add the following query to this file to retrieve all blog posts: ```graphql filename="./queries/get-posts.graphql" copy query { Posts { items { _id _slug title } } } ``` > **Note:** > > You can create and test GraphQL queries using the [Apollo explorer](https://studio.apollographql.com/sandbox/explorer) from Prepr. Open the API Explorer from the *Post* content item page in Prepr or the access token page. If you’re using preloaded demo data in your Prepr CMS environment as mentioned above in the [Prerequisites](#prerequisites) section, you should have a few published blog posts as shown in the below image. The query will retrieve the *ID*, *Slug*, and *Title* of each blog post. ![demo blog posts](https://assets-site.prepr.io/6ayuw6ta7ccw//content-list-page-filtered-on-posts-for-qsgs.png) In the next step, we'll fetch and process the query response. ### Fetch multiple blog posts Now that the query has been added, fetch the blog posts from Prepr and display them in the app. 1. Open the `index.php` file in the `public` folder and add the code below to display the data retrieved from the query. ```php filename="./public/index.php" copy {11-33} '); echo '
'; echo '

My blog site

'; // Display a list echo '
    '; // Make a query request for a list of blog posts $apiRequest ->query('../queries/get-posts.graphql') ->request(); $apiResponse = $apiRequest->getResponse(); $posts = $apiResponse['data']['Posts']['items']; if ($posts) { foreach ($posts as $post) { // Display each post returned by the query echo '
  • '.$post['title'].'
  • '; } } echo '
'; echo '
'; ``` Now when you view the website on your localhost, you'll see something like the image below. ![blog post overview](https://assets-site.prepr.io/1h1i3di3fxkj//php-blog-site-post-list.png) ### Fetch individual blog posts Now that you have the list of blog posts, add links to them. When a visitor clicks on a link, your app should open a detailed blog post page automatically. The instructions below show you how to set up the routing from the main page to the detailed page and how to fetch the post details based on the slug of the post that was clicked. Add another query to fetch a specific blog post by its slug and make this page visible when clicking a blog post. 1. Create a file called `get-post-by-slug.graphql` in the `queries` folder and add the following to query a specific blog post by its slug: ```graphql filename="./queries/get-post-by-slug.graphql" copy query ($slug: String) { Post (slug: $slug) { _id title cover { url(width: 300, height: 250) } content { __typename ... on Text { _id body text } ... on Assets { items { _id url(width: 300, height: 250) } } } } } ``` Now that the query is added, fetch the individual post by its slug. Fetch the blog post title, cover image and the post content. > **Note:** > > The *Post* content is stored in a Dynamic content field. Check out the GraphQL docs for more details on [how to fetch the data within this field](https://docs.prepr.io/graphql-api/schema-field-types-dynamic-content-field). 2. Open the `index.php` file in the `public` folder again and add a condition to display a specific blog post when the slug is filled. ```php filename="./public/index.php" copy {11-14,35-75} '); echo '
'; echo '

My blog site

'; // Show the list of blog posts if there is no slug if(!isset($_GET['slug'])) {
    '; $apiRequest ->query('../queries/get-posts.graphql') ->request(); $apiResponse = $apiRequest->getResponse(); $posts = $apiResponse['data']['Posts']['items']; if ($posts) { foreach ($posts as $post) { echo '
  • '.$post['title'].'
  • '; } } echo '
'; // Retrieve a post by the slug and show the details } else { $apiRequest ->query('../queries/get-post-by-slug.graphql') ->variables([ 'slug' => $_GET['slug'] ]) ->request(); $apiResponse = $apiRequest->getResponse(); $post = $apiResponse['data']['Post']; if($post) { echo '

' . $post['title'] . '

'; echo '
'; if($post['content']) { foreach($post['content'] as $content) { if($content['__typename'] === 'Assets') { echo '
'; } elseif($content['__typename'] === 'Text') { echo '
' . $content['body'] . '
'; } } } } } echo '
'; ``` Now, when you view your site, you can click a blog post to open that specific post like in the image below. ![blog post detail](https://assets-site.prepr.io/3talvap6ozar//php-blog-post-detail-page.png) ## All done Congratulations! You have successfully connected a PHP project to Prepr for a simple Blog app. ## Next steps To learn more on how to expand your project, check out the following resources: - [Draft more queries for components and other field types](https://docs.prepr.io/graphql-api) - [How to install and use Tailwind CSS](https://tailwindcss.com/docs/installation) Source: https://docs.prepr.io/connecting-a-front-end-framework/php/php-quick-start-guide --- # PHP + Prepr CMS Need info on Prepr for a PHP app? Look no further. This all-in-one page gives you all the resources you need to connect your project to Prepr. ## PHP Quick start guide - [Follow the guide](https://docs.prepr.io/connecting-a-front-end-framework/php/php-quick-start-guide): The quick start guide covers the basics of connecting Prepr CMS with PHP to make a basic blog in less than 10 minutes. - [Visit the GitHub repo](https://github.com/preprio/php-quick-start): Check out the source code and clone the repo for a quick PHP project set up. ## PHP SDKs - [PHP GraphQL SDK](https://docs.prepr.io/php/php-graphql-sdk): Visit the GitHub repository for the GraphQL provider. - [PHP REST API SDK](https://docs.prepr.io/php/php-rest-sdk): Visit the GitHub repository for the REST provider. ## Other resources - [How to set up personalization](https://docs.prepr.io/personalization/setting-up-personalization) - [How to measure A/B testing](https://docs.prepr.io/ab-testing/setting-up-ab-testing) Source: https://docs.prepr.io/connecting-a-front-end-framework/php --- # Svelte Quick start guide *Estimated duration: 10 minutes* *This guide shows you how to connect Prepr to a Svelte project to get data from Prepr CMS. You'll learn how to make a simple blog with Svelte and Prepr CMS. By the end of this guide, you'll have a working app that looks like the image below.* ![Blog site end result](https://assets-site.prepr.io/74bgrcumcy41//svelte-blog-posts.png) > **Note:** > > If you want to skip ahead, try out [the zero installation demo on Stackblitz](https://stackblitz.com/~/github.com/preprio/svelte-quick-start) or clone the [repository on GitHub](https://github.com/preprio/svelte-quick-start) to run the demo locally. ## Prerequisites You need to have the following setup before you connect your Svelte project to Prepr. - [A free Prepr account](https://signup.prepr.io) - [An environment with demo data in Prepr](https://docs.prepr.io/project-setup/setting-up-environments#create-an-environment) - [The latest version of Node.js](https://nodejs.org/en) ## Create a simple Blog website with Svelte and Prepr CMS ### Create a new Svelte project The instructions below will guide you on how to create an empty Svelte project for your blog app. If you have an existing Svelte project, then you can skip this step. 1. Execute the command below to create a new Svelte project called `prepr-svelte`. Choose `SvelteKit minimal`, `No` for TypeScript and defaults for the remaining options. ```bash copy npx sv create prepr-svelte ``` 2. When the project is successfully created, go to the `prepr-svelte` folder, the root directory of the project, and execute the commands below to start the project. ```bash copy cd prepr-svelte ``` ```bash copy npm run dev ``` 3. You should now be able to view your app on your localhost, for example, `http://localhost:5173/`. 4. Open your Svelte project with your preferred code editor. 5. Go to the `src/routes` folder and update the `+page.svelte` file with the following code to display your blog: ```xml filename="./src/routes/+page.svelte" copy My blog site

My blog site

``` You should now see something like the image below on your localhost. ![Svelte empty blog site](https://assets-site.prepr.io/4n9f2dg05f1m//svelte-empty-blog-site.png) ### Connect to Prepr Set up a connection to Prepr to retrieve content with the Prepr GraphQL API. The instructions below show you how to connect to Prepr directly from your project so that you can execute GraphQL queries to request data from the Prepr API. 1. Go to the `src/lib` folder and create a file called `prepr.js` in this folder. Copy the following code to this file to set up the endpoint: ```js filename="./src/lib/prepr.js" copy import { PREPR_ENDPOINT } from '$env/static/private' const prepr = async (query, variables) => { const response = await fetch(PREPR_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query, variables }), }) return response } export default prepr ``` 2. We recommend using environment variables to store sensitive information like access tokens. To add environment variables, create a `.env` file in the root directory of your project and add the API URL as follows: ```bash filename="./.env" copy PREPR_ENDPOINT= ``` 3. Replace the placeholder value `` with the API URL of an access token from Prepr. Get this URL by logging into your Prepr account: a. Click the icon and choose the **Access tokens** option to view all the access tokens. b. Copy the *API URL* value from the *GraphQL Production* access token to only retrieve published content items. ![GraphQL production access token example](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) > **Note:** > > Use the *GraphQL Production* API URL to request published content items for your live app and use the *GraphQL Preview* value to make a preview of unpublished content items for your content editors. If your app runs without errors, then the setup above was done correctly. The next step is to fetch content from Prepr using the endpoint that you set up. ### Add a GraphQL query Now that your app is connected to Prepr, fetch the blog posts by adding a GraphQL query. 1. Create a `queries` directory in the `src/lib` folder and create a file named `get-posts.js`. 2. Add the following query to this file to retrieve all blog posts: ```js filename="./src/lib/queries/get-posts.js" copy const GetPosts = ` query { Posts { items { _id _slug title } } } ` export default GetPosts ``` > **Note:** > > You can create and [test GraphQL queries](https://docs.prepr.io/graphql-api/test-queries) using the [Apollo explorer](https://studio.apollographql.com/sandbox/explorer) from Prepr. Open the **API Explorer** from the *Post* content item page in Prepr or the access token page. If you’re using preloaded demo data in your Prepr CMS environment, as mentioned above in the [Prerequisites](#prerequisites) section, you should have a few published blog posts as shown in the below image. The query will retrieve the *ID*, *Slug*, and *Title* of each blog post. ![demo blog posts](https://assets-site.prepr.io/6ayuw6ta7ccw//content-list-page-filtered-on-posts-for-qsgs.png) In the next step, we'll fetch and process the query response. ### Fetch multiple blog posts Now that the query has been added, fetch the blog posts from Prepr and display them in the app. 1. Go to the `src/routes` folder and create a file called `+page.server.js` to execute the query that you just created as follows: ```js filename="./src/routes/+page.server.js" copy import prepr from '$lib/prepr' import GetPosts from '$lib/queries/get-posts.js' export async function load() { const response = await prepr(GetPosts) const { data } = await response.json() const { items } = data.Posts return { posts: items } } ``` 2. Next, update the `+page.svelte` file to display the retrieved data by adding the following HTML elements: ```xml filename="./src/routes/+page.svelte" copy {1-3, 14-20} My blog site

My blog site

``` Now when you view the website on your localhost, you'll see something like the image below. ![Svelte blog posts](https://assets-site.prepr.io/74bgrcumcy41//svelte-blog-posts.png) ### Fetch individual blog posts Now you have the list of blog posts with links to them. When a visitor clicks a blog post link, your app should open a detailed post page automatically. However, we haven't created the detailed page yet. Now, when you click the link, a new page opens with the slug in the URL, but a Page 404 error is displayed. The instructions below show you how to fetch the blog post details based on the slug of the post that was clicked. Add another query to fetch a specific blog post by its slug and make this page visible when clicking a link. 1. Create a file called `get-post-by-slug.js` in the `queries` folder and add the following query to this file to query a specific blog post by its slug: ```js filename="./src/lib/queries/get-post-by-slug.js" copy const GetPostBySlug = ` query ($slug: String) { Post (slug: $slug) { _id title cover { url(width: 300, height: 250) } content { __typename ... on Text { _id body text } ... on Assets { items { _id url(width: 300, height: 250) } } } } } ` export default GetPostBySlug ``` Now that the query is added, fetch the individual blog post by its slug. Fetch the title, cover image and the post content. > **Note:** > > The *Post* content is stored in a *Dynamic content field*. Check out the GraphQL docs for more details on [how to fetch the data within this field](https://docs.prepr.io/graphql-api/schema-field-types-dynamic-content-field). 2. Go to `src/routes/` and create a folder called `blog`, a subfolder called `[slug]`, and a new `+page.server.js` file with the following code to execute the query that you just created: ```js filename="./src/routes/blog/[slug]/+page.server.js" copy import { error } from '@sveltejs/kit' import prepr from '$lib/prepr' import getPostBySlug from '$lib/queries/get-post-by-slug.js' export async function load({ params }) { const response = await prepr(getPostBySlug, {slug: `blog/${params.slug}`}) const { data } = await response.json() if (!data.Post) { error(404, 'Post not found') } return { post: data.Post } } ``` 3. Create a new `+page.svelte` in the same folder to display the blog post detail page as follows: ```xml filename="./src/routes/blog/[slug]/+page.svelte" copy {data.post.title}

{data.post.title}

{#each data.post.content as content}
{#if content.__typename === 'Assets'} {:else if content.__typename === 'Text'} {@html content.body} {/if}
{/each}
``` Now, when you view your site, you can click a blog post link to open that specific blog post like in the image below. ![Blog post detail page](https://assets-site.prepr.io/3mejcavoloz6//svelte-blog-post-detail-page.png) ## All done Congratulations! You have successfully connected a Svelte project to Prepr for a simple Blog app. ## Next steps To learn more on how to expand your project, check out the following resources: - [Draft more queries for components and other field types](https://docs.prepr.io/graphql-api) - [How to install and use Tailwind CSS](https://tailwindcss.com/docs/guides/vite) - [Deploy your Svelte app with Vercel](https://svelte.dev/docs/kit/adapter-vercel) Source: https://docs.prepr.io/connecting-a-front-end-framework/svelte/svelte-quick-start-guide --- # Gatsby Quick start guide *Estimated duration: 10 minutes* *This guide shows you how to connect Prepr to a Gatsby project to get data from Prepr CMS. You'll learn how to make a simple blog with Gatsby and Prepr CMS. By the end of this guide, you'll have a working app that looks like the image below.* ![Blog site end result](https://assets-site.prepr.io/4sptbzci3cja//gatbsy-blog-site-posts.png) > **Note:** > > If you want to skip ahead, clone the [repository on GitHub](https://github.com/preprio/gatsby-quick-start) to run the demo locally. ## Prerequisites You need to have the following setup before you connect your Gatsby project to Prepr. - [A free Prepr account](https://signup.prepr.io) - [An environment with demo data in Prepr](https://docs.prepr.io/project-setup/setting-up-environments#create-an-environment) - [The latest version of Node.js](https://nodejs.org/en) > **Note:** > > The latest Gatsby installation generates warnings with the latest version of node for using a deprecated module. > For the scope of this project, you can ignore those warnings. ## Create a simple Blog website with Gatsby and Prepr CMS ### Create a new Gatsby project The instructions below will guide you on how to create an empty Gatsby project for your blog app. If you have an existing Gatsby project then you can skip this step. 1. Execute the command below to create a new Gatsby project. ```bash copy npm init gatsby ``` Enter the options as shown in in the image below to create the `prepr-gatsby` project structure used in this guide. ![gatsby options](https://assets-site.prepr.io/4yaelqy4muls//install-gatsby-project-blog-site.png) 2. When the project is successfully created, execute the commands below to go to the `prepr-gatsby` folder, the root directory of the project, and to start the project. ```bash copy cd prepr-gatsby ``` ```bash copy npm run develop ``` 3. You should now be able to view your app on your localhost, for example, `http://localhost:8000/`. 4. Open your Gatsby project with your preferred code editor. 5. Go to the `src/pages` folder and replace the contents of the `index.js` file with the following code to display your blog: ```js filename="./src/pages/index.js" copy import * as React from "react" const IndexPage = () => { return (

My blog site

) } export default IndexPage export const Head = () => My blog site ``` You should now see something like the image below on your localhost. ![Empty Gatsby blog site](https://assets-site.prepr.io/47aag5vq20ke//empty-gatsby-blog-site.png) ### Connect to Prepr Set up a connection to Prepr to retrieve CMS data with GraphQL. The instructions below show you how to connect to Prepr directly from your project so that you can execute GraphQL queries to request data from the Prepr API. 1. For this project we use the `gatsby-source-graphql` data layer to connect to Prepr. Stop the server you started in the above step (`CTRL-C`) and execute the following command in the terminal to install the data layer: ```bash copy npm install gatsby-source-graphql ``` 2. We recommend using environment variables to store sensitive information like access tokens. To add environment variables, create a `.env` file in the root directory of your project and add the API URL as follows: ```bash filename="./.env" copy PREPR_GRAPHQL_URL= ``` 3. Replace the placeholder value `` with the API URL of an access token from Prepr. This URL includes the access token for the environment you want to query from. Get this URL by logging into your Prepr account:\ a. Click the icon and choose the **Access tokens** option to view all the access tokens.\ b. Copy the *API URL* value from the *GraphQL Production* access token to only retrieve published content items. ![GraphQL production access token example](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) > **Note:** > > Use the *GraphQL Production* API URL to request published content items for your live app and use the *GraphQL Preview* value to make a preview of unpublished content items for your content editors. 4. Add a plugin for the Gatsby data layer and make the `.env` file visible to the project by updating the `gatsby-config.js` file with the following configuration: ```js filename="./gatsby-config.js" copy /** * @type {import('gatsby').GatsbyConfig} */ // Make the .env file available to the project require("dotenv").config({ path: `.env`, }) module.exports = { siteMetadata: { title: `My blog site`, siteUrl: `https://www.yourdomain.tld`, }, // Include a plugin for the Gatsby data layer plugins: [ { resolve: "gatsby-source-graphql", options: { typeName: "Prepr", fieldName: "prepr", url: process.env.PREPR_GRAPHQL_URL, } } ], } ``` The next step is to fetch content from Prepr using the Gatsby data layer. ### Fetch multiple blog posts Now that your app is connected to Prepr, fetch the blog posts from Prepr. 1. Go to the `src/pages` folder and update the `index.js` to add a query to retrieve all blog posts and display them in the front end. ```js filename="./src/pages/index.js" copy import * as React from "react" import {graphql, useStaticQuery} from "gatsby"; const IndexPage = () => { const preprData = useStaticQuery(graphql` query { prepr { Posts { items { _id _slug title } } } } `) const posts = preprData.prepr.Posts return (

My blog site

) } export default IndexPage export const Head = () => Home Page ``` > **Note:** > > You can create and [test GraphQL queries](https://docs.prepr.io/graphql-api/test-queries) using the [Apollo explorer](https://studio.apollographql.com/sandbox/explorer) from Prepr. Open the **API Explorer** from the *Post* content item page in Prepr or the access token page. If you’re using preloaded demo data in your Prepr CMS environment as mentioned above in the [Prerequisites](#prerequisites) section, you should have a few published blog posts as shown in the below image. The query will retrieve the *ID*, *Slug*, and *Title* of each blog post. ![demo blog posts](https://assets-site.prepr.io/6ayuw6ta7ccw//content-list-page-filtered-on-posts-for-qsgs.png) Rerun the server with the command below. If your app runs without errors, then the setup above was done correctly. ```bash copy npm run develop ``` Now when you view the website on your localhost, you'll see something like the image below. ![Blog site - list of posts](https://assets-site.prepr.io/4sptbzci3cja//gatbsy-blog-site-posts.png) ### Fetch individual blog posts Now you have the list of blog posts with links to them. When a visitor clicks a blog post link, your app should open a detailed post page automatically. However, we haven't created the detailed page yet. Now, when you click the link, a new page opens with the slug in the URL, but a *Page 404* error is displayed. The instructions below show you how to set up the routing from the main page to the detailed page and how to fetch the blog post details based on the slug of the blog post that was clicked. 1. Go to the `src` folder and create a new subfolder called `templates`. In this folder create the `post.js` page with the query to retrieve a specific blog post by its slug. ```js filename="./src/templates/post.js" copy import * as React from "react" import {graphql} from "gatsby"; const PostPage = ({data}) => { const post = data.prepr.Post return (

{post.title}

{post.content.map((content,index) => (
{ content.__typename === "Prepr_Assets" && {post.title} } { content.__typename === 'Prepr_Text' &&
}
))}
) } export const query = graphql` query($slug: String!) { prepr { Post (slug: $slug) { _id title cover { url(width: 300, height: 250) } content { __typename ... on Prepr_Text { _id body text } ... on Prepr_Assets { items { _id url(width: 300, height: 250) } } } } } } ` export default PostPage export const Head = () => Post Page ``` Now that the query is added, fetch the individual blog post by its slug. Fetch the blog post title and the content. > **Note:** > > The *Post* content is stored in a *Dynamic content field*. Check out the GraphQL docs for more details on [how to fetch the data within this field](https://docs.prepr.io/graphql-api/schema-field-types-dynamic-content-field). > **Note:** > > **Gatsby source plugin behavior** > > In the `./gatsby-config.js` configuration, you set a prefix, `Prepr`, for each of the `typename` values to distinguish between multiple sources. > Don't forget to remove these prefixes when testing these queries directly in Prepr through the [Apollo explorer](https://studio.apollographql.com/sandbox/explorer). 2. Go to the root directory and create a new config file called `gatsby-node.js`. Add the following configuration to this file to define dynamic pages for each blog post: ```js filename="./gatsby-node.js" copy // Retrieve the blog post slug to create dynamic pages exports.createPages = async ({ actions, graphql }) => { const { data } = await graphql(` query { prepr { Posts { items { _slug } } } } `) data.prepr.Posts.items.forEach( (item) => { const slug = item._slug actions.createPage({ path: slug, component: require.resolve(`./src/templates/post.js`), context: { slug }, }) }) } ``` Stop the server you started before (`CTRL-C`) and execute the command below in the terminal. If your app runs without errors, then the setup above was done correctly. ```bash copy npm run develop ``` Now, when you view your site, you can click a blog post link to direct you to that specific blog post like in the image below. ![Gatsby blog site - detailed post page](https://assets-site.prepr.io/4fh897igp92m//gatsby-blog-site-detailed-post-page.png) ## All done Congratulations! You have successfully connected a Gatsby project to Prepr for a simple Blog app. ## Next steps To learn more on how to expand your project, check out the following resources: - [Draft more queries for components and other field types](https://docs.prepr.io/graphql-api) - [How to install and use Tailwind](https://tailwindcss.com/docs/guides/vite) Source: https://docs.prepr.io/connecting-a-front-end-framework/gatsby/gatsby-quick-start-guide --- # Svelte + Prepr CMS Need info on Prepr for a Svelte app? Look no further. This all-in-one page gives you all the resources you need to connect your project to Prepr and to set up personalized pages exactly the way your marketers want them. ## Svelte Quick start guide - [Continue with the guide](https://docs.prepr.io/connecting-a-front-end-framework/svelte/svelte-quick-start-guide): The quick start guide covers the basics of connecting Prepr CMS with Svelte to make a basic blog in less than 10 minutes. - [Visit the GitHub repo](https://github.com/preprio/svelte-quick-start): Check out the source code and clone the repo for a quick Svelte project set up. - [Try it yourself on Stackblitz](https://stackblitz.com/~/github.com/preprio/svelte-quick-start): Check out the source code and play around to learn more. ## Prepr Toolkit The Prepr Toolkit enables you to set up A/B testing, personalization, a preview bar, and supports the live preview in Prepr - allowing you to seamlessly test and optimize content for different audience segments in your staging environment. - [Visit the GitHub repo](https://github.com/preprio/prepr-toolkit): Check out the source code and basic steps to install the Prepr Toolkit. ## Other resources - [Rendering strategies, SSR or SSG?](https://docs.prepr.io/development/best-practices/csr-ssr-ssg) - [How to set up personalization](https://docs.prepr.io/personalization/setting-up-personalization) - [How to measure A/B testing](https://docs.prepr.io/ab-testing/setting-up-ab-testing) Source: https://docs.prepr.io/connecting-a-front-end-framework/svelte --- # Working with assets Follow these guidelines to help you include images, videos, audios, and files in your web applications. - [Images](https://docs.prepr.io/development/best-practices/assets/images): Explore how to add images to your web application using Prepr. - [Video & Audio](https://docs.prepr.io/development/best-practices/assets/video-audio): Learn how to post videos and audios to your web application from Prepr. - [Live video stream](https://docs.prepr.io/development/best-practices/assets/live-video-stream): Get a walkthrough on streaming your live video in a web application using Prepr. - [Files](https://docs.prepr.io/development/best-practices/assets/files): Check out how to include files in your web application with Prepr. Source: https://docs.prepr.io/development/best-practices/assets --- # CSR/SSR/SSG Rendering strategies *This article explains different rendering strategies for front-end apps and makes recommendations on how to implement your strategy with Prepr.* ## Introduction It's important to decide on a rendering strategy for your front-end apps in the early stages of your implementation. The strategy you choose should match the type of content that your front end renders. For example, do you need to render a lot of static content such as documentation or urgent dynamic content like on an ecommerce site. We look at different types of rendering and any special considerations when implementing your front end alongside Prepr. ## Rendering types |Rendering type| Applicable Technologies | |-------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| |[Client-side rendering (CSR)](#client-side-rendering-csr) \ The original way to render web pages with dynamic content when JavaScript was first introduced. When a user visits a page, they see it only after the script has finished.| [React](https://docs.prepr.io/connecting-a-front-end-framework/react), [Vue.js](https://docs.prepr.io/connecting-a-front-end-framework/vuejs), [Angular](https://docs.prepr.io/connecting-a-front-end-framework/angular), \ [Next.js](https://docs.prepr.io/connecting-a-front-end-framework/nextjs), [Nuxt](https://docs.prepr.io/connecting-a-front-end-framework/nuxtjs). | |[Server-side rendering (SSR)](#server-side-rendering-ssr) \ The server generates the HTML on a request for a page. This means that when a user navigates to a page, they will see a fully rendered UI immediately.| [React](https://docs.prepr.io/connecting-a-front-end-framework/react), [Vue.js](https://docs.prepr.io/connecting-a-front-end-framework/vuejs), [Angular](https://docs.prepr.io/connecting-a-front-end-framework/angular), [Next.js](https://docs.prepr.io/connecting-a-front-end-framework/nextjs), [Nuxt](https://docs.prepr.io/connecting-a-front-end-framework/nuxtjs), \ [Node.js](https://docs.prepr.io/connecting-a-front-end-framework/nodejs). | |[Static site generation (SSG)](#static-site-generation-ssg)\ A bunch of static files for the entire site are generated during deployment. This means that a visitor can see and interact with the pages immediately.| [React](https://docs.prepr.io/connecting-a-front-end-framework/react), [Vue.js](https://docs.prepr.io/connecting-a-front-end-framework/vuejs), [Angular](https://docs.prepr.io/connecting-a-front-end-framework/angular), [Next.js](https://docs.prepr.io/connecting-a-front-end-framework/nextjs), [Nuxt](https://docs.prepr.io/connecting-a-front-end-framework/nuxtjs), \ [Node.js](https://docs.prepr.io/connecting-a-front-end-framework/nodejs). | |[Hybrid rendering solutions](#hybrid-rendering-solutions) \ Some frameworks offer a hybrid rendering solution that uses the best of both SSR and SSG. | [See below](#hybrid-rendering-solutions) for more details on the hybrid solutions offered by [Next.js](https://docs.prepr.io/connecting-a-front-end-framework/nextjs), [Nuxt](https://docs.prepr.io/connecting-a-front-end-framework/nuxtjs). | ## How to choose a rendering type It is important to think about how frequently your content changes and how urgent these content changes are. For example, a blog site does not usually require multiple updates in a day, so static generation is a good option. On the other hand, product information on an ecommerce site needs frequent updates, so rendering needs to happen more dynamically. > **Note:** > > **Special considerations for Prepr** > > Personalization and A/B testing are dynamic by nature so they work out of the box with SSR. > Check out the [caching strategies doc](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/caching-strategies) for more details. Below are other metrics to consider when you choose a rendering strategy. |Metric|CSR|SSR|SSG|Hybrid| |----------|-------|-------|-------|-------| |**Data integrity** \ How up to date the data is when a page loads.| | | | | |**SEO** \ How easy it is for search engines to find the page content.| | | | | |**Performance** \ How quickly a page loads. | | | | | |**Build Time** \ How quick it is to build an app. | | | | | > **Note:** > > Data integrity and the build time for hybrid rendering solutions can be comparable to the SSR metrics depending on the type of hybrid solution and how exactly it is implemented. Watch the video below for the key differences betweens SSG and SSR. [Watch video](https://player.mux.com/01yhTeW1VGdfDqCjViAdmXaZh023gh65u6xqGvi8nAj7U) *This video was created using AI avatars and voices to ensure a consistent look and feel. The use of AI matches our culture of using innovative technologies.* ## Client-side rendering (CSR) Client-side rendering (CSR) is rendering pages directly in the browser for every page request using JavaScript. This type of rendering is mainly used for single-page applications (SPA) because the single page needs to be refreshed each time it's loaded. This means the data is always up to date, but large payloads will slow down the loading of a page. ![csr](https://assets-site.prepr.io//6yqki00jnsu9-csr.png) ## Server-side rendering (SSR) Server Side Rendering (SSR), also known as dynamic rendering, is when the HTML of a site is generated on the server, then sent to the browser. This type of rendering is faster than CSR for large payloads and is useful for SEO purposes, because search engines can access the content. See a simple SSR process flow below. ![ssr](https://assets-site.prepr.io//1ogtlzw4lc64-ssr.png) ## Static site generation (SSG) SSG is a type of pre-rendering where the process of compiling and rendering a web app happens during the build time. The output of the build is a bunch of static files, including the HTML file as well as assets like JavaScript and CSS. Updates to pages are not rendered until a new build. Because all the pages are pre-rendered, the page load is faster than SSR, but any changes to content will not be re-rendered on the fly. ![ssg](https://assets-site.prepr.io//5ktab0u08oap-ssg.png) ## Hybrid rendering solutions A hybrid rendering solution gives developers the option to get benefits from both SSR and SSG. The following solutions are available in a few frameworks: - **Page-by-page**: This means that each page of your project can be rendered either statically or dynamically. This method is available in Next.js. Check out the [Next.js](https://nextjs.org/docs/pages/building-your-application/rendering) docs for more details. - **Route-based rendering**: This solution allows you to render specific routes (URL paths) statically or dynamically. The route can be at subdomain level which means that groups of pages can be rendered statically or dynamically. Check out the [Next.js docs](https://nextjs.org/learn-pages-router/basics/dynamic-routes) and the [Nuxt docs](https://nuxtjs.org/docs/features/rendering-modes) for more details. - **Incremental static regeneration (ISR)** and **Deferred static generation (DSG)**: These hybrid solutions aim to use the best of both SSR and SSG. When a user visits a page, the front end triggers the regeneration (also known as revalidation) of the page, but the user sees the *static page* immediately. If the user or another user visits the same page a few seconds later, the newer page is rendered. Unlike SSG, you can choose the number of pages to build to reduce the build time and performance is better than SSR because the request does not wait for the rendering of a page to complete. ISR was developed by Next.js on Vercel while DSG is the Gatsby solution. Another version of this solution is possible in Nuxt deployed on Layer0. See a simple process flow below: ![isr](https://assets-site.prepr.io//7gcjsbh0l2pb-isr.png) ## Schedule a free consultation Do you still have questions on rendering strategies in relation to Prepr? Book a 15-minute call with a Prepr solution engineer right away. [Schedule a call](https://prepr.io/get-a-demo) Source: https://docs.prepr.io/development/best-practices/csr-ssr-ssg --- # Handling redirects *This article explains how to handle redirects for your content items in Prepr CMS* ## Introduction Redirects send visitors from a requested URL to another. You often set up redirects in your framework-specific front-end app or in a redirects file for a hosting provider for pages that are broken or pages that were moved to new URLs to ensure visitors and search engines access the most relevant or current page. ## Handle redirects with a headless CMS It's only natural that content items can change. When the changes include the URL that links to the content item, this results in a broken link. To prevent this situation, a developer sets up a redirect to display a working page when an outdated link is visited. In the case where you have content items that could potentially have changes to the URL that links to it, for example, when updating an existing article with fresher content including the name of the link to the article, follow the steps below to reduce the number of broken links in your front end. ## Step 1: Model the redirect in Prepr CMS The first step is to model the redirect to make it possible to manage redirect entries directly in Prepr CMS. The *Redirect* model includes the following fields: - The outdated URL, often known as the *Source*. - The new target URL, known as the *Destination*. - An indicator to choose the type of redirect, for example, `permanent` or `temporary`. See the examples below for typical redirect config in a front-end framework like Next.js and for server redirect config like a Vercel deployment. **Next.js** ```js filename="next.config.js" copy module.exports = { async redirects() { return [ { source: '/blog/car-leasing-101', destination: '/blog/car-leasing-101-what-you-need-to-know-before-you-sign', permanent: true, }, ] }, } ``` **Vercel** ```json filename="vercel.json" copy "redirects": [ { "source": "/blog/car-leasing-101", "destination": "https://example.com/blog/car-leasing-101-what-you-need-to-know-before-you-sign", "permanent": true } ] ``` A slug is the end part of a URL after the last backslash and identifies a unique page in a front end. In Prepr CMS, you can create a [slug field](https://docs.prepr.io/content-modeling/field-types#slug-field) in a model to maintain a unique value for each content item. This slug value is then used in your front-end app to set up page routing to that content item. Check out the [Next Quick start guide](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-quick-start-guide#step-4-fetch-individual-articles) for an example implementation. With this in mind, follow the steps below to create a *Redirect* model. 1. Click the **Schema** tab to open the **Schema Editor**. 2. Then, click the **+ Add model** button. 3. Click **Multi-item model** and **Next** again. 4. Enter *Redirect* as the *Name* and click **Save**. 5. Add a *Text* field type to save the *Internal title* to easily identify a redirect record. 6. Drag and drop the *Slug* field type into your model. Switch to the **Validation** tab and enable the **This field is required** toggle. 7. Drag and drop the *Content reference* field type, from the list on the right into your model.\ a. Enter *Destination* as the *Name*.\ b. Select the models you want to allow to have redirects.\ c. Switch to the **Validation** tab and enable the **This field is required** toggle.\ d. Click the **Save** button. 8. Drag and drop the *Boolean* field type into your model and enter *Permanent* as the *Name*, set a *Default value* of **True** and click the **Save** button. ![Redirect model](https://assets-site.prepr.io/239anu6du3oo//redirect-model-example.png) Now that the *Redirect* model is done, move on to the next step to add redirect entries in Prepr CMS. > **Note:** > > In this example, we are creating redirects to content items. If you need a more generic redirect solution you should add a *Text* field for the destination URL instead of the *Content Reference* field. ## Step 2: Create a redirect in Prepr CMS Follow the instructions below to add a redirect entry manually. 1. Go to the **Content** tab. 2. Click **Add item** and select the **Redirect** model created in the previous step. 3. Enter the *Slug* of the outdated URL in the format expected by your framework or host redirects file. 4. For the *Destination* field, choose the relevant content item for which the slug was changed. 5. Enable the permanent field, if applicable, and click the **Publish** button. ![Example Redirect content item](https://assets-site.prepr.io/1iprt23lahg8//example-redirect-item.png) That's it! Once the redirect entries are added to Prepr, you can then fetch the entries and process them. ## Step 3: Handle redirects This guide outlines two methods to handle redirects. One is based on handling redirects in a Next.js project by dynamically adding the redirects to the `next.config.js` file. The other is handling redirects in an Astro project by adding the redirects to the `vercel.json` which gets picked up and processed when the project is deployed in Vercel. Follow the steps below to create a script to fetch the redirects info from Prepr CMS and generate the redirects to either be included in the `next.config.js` or written to the `vercel.json`. 1. Create the script `getRedirects` to fetch the redirects like in the code snippets below. Set the placeholder value for `` to the API URL from a [Prepr access token](https://docs.prepr.io/graphql-api/authorization#access-tokens). **Next.js** ```js filename="./getRedirects.js" copy module.exports = async () => { const response = await fetch("", { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ /* The query below is based on the Redirects model having a content reference to a single model (Article) */ query: ` { Redirects { items { _slug destination { _slug } permanent } } }` }) }); const {data} = await response.json(); const parsedRedirects = data.Redirects.items.map( (element) => { return { source: element._slug, destination: `/articles/${element.destination[0]._slug}`, permanent: element.permanent } } ) return parsedRedirects; } ``` **Astro** ```js filename="./src/functions/getRedirects.js" copy const response = await fetch("", { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ /* The query below is based on the Redirects model having a content reference to a single model (Article) */ query: ` { Redirects { items { _slug destination { _slug } permanent } } }` }) }); const {data} = await response.json(); const parsedRedirects = data.Redirects.items.map( (element) => { return { source: element._slug, destination: `/articles/${element.destination[0]._slug}`, permanent: element.permanent } } ) return parsedRedirects; } ``` 2. Use one of two methods to update the configuration file to handle the redirects. - **Redirect with Next.js**: Add the redirects from Prepr to the built-in `next.config.js` file. - **Redirect with Vercel**: Add the redirects from Prepr to the `vercel.json` file during build time so that Vercel processes them after deployment. **Next.js** ```js filename="next.config.js" copy const getRedirects = require('./get-redirects'); module.exports = { // Define the redirects with output from the previous script async redirects() { return getRedirects(); }, ... } ``` **Astro** ```js filename="astro.config.mjs" copy const redirects = await getRedirects() // Writes the fetched redirects to an existing vercel.json file await fs.writeFile('./vercel.json', JSON.stringify(redirects),(err) => { if (err) console.log(err); }); // https://astro.build/config }); ``` That's it! Once you successfully build and deploy your project, the redirects are processed and can be tested by navigating to the outdated links. ## What's next? Check out our other best practice guides from the list below: - [Rendering strategies](https://docs.prepr.io/development/best-practices/csr-ssr-ssg) - [Using TypeScript with the GraphQL API](https://docs.prepr.io/development/best-practices/typescript) - [How to set up content item previews](https://docs.prepr.io/project-setup/setting-up-previews-and-visual-editing) Source: https://docs.prepr.io/development/best-practices/redirects --- # Handling SEO in a headless CMS *This article details how to solve SEO challenges when implementing a headless CMS.* ## Introduction SEO is the process of making a website easy to find through search engines like Google. So, setting up SEO measures is a necessary activity when implementing a front end. And this process has been made easier with the introduction of the CMS. Headless CMSs are becoming the more popular choice over traditional CMSs because of consistency across multiple platforms, flexibility, improved performance, and scalability. Despite these benefits, the very nature of a headless CMS that decouples the front end from the content means that there are some challenges when taking measures to improve SEO. ## SEO challenges with a headless CMS Traditional CMS platforms often come with built-in SEO tools that headless CMSs don't usually have. This means that developers need to implement an SEO strategy in the front-end application to deal with challenges when implementing a headless CMS. When you prepare an SEO strategy, consider the following topics: - Managing metadata - Setting up URLs - Managing redirects - Generating a sitemap - Reviewing content for SEO Let's look at each of these SEO topics in detail and how to handle them in Prepr CMS. ### Managing Metadata Metadata are elements like title and meta description with keywords that provide information about a webpage to search engines and web browsers. These elements are placed in the head HTML of a webpage and are used to help search engines understand the content and context of the page. Unlike headless CMSs, traditional CMSs usually include plugins that can customize and update meta tags for pages. So, some once-off development effort is needed to set these up dynamically in a headless CMS. #### How to manage metadata in a headless CMS Headless CMSs decouple content from the front-end presentation. Before implementing a headless CMS project, you will typically model the content first. During this stage you will model the SEO metadata. ![SEO component](https://assets-site.prepr.io//62nau9j7ep4d-seo-component.jpg) Important metadata include fields like the `Meta title` and `Meta description` like in the example above. For the purposes of sharing content on social media, you could also include a `Meta image`. You could even include a `priority` to indicate the pages that are most important for crawling and indexing. Use indicators like `nofollow` and `noindex` for content that should be ignored by searchbots. Use the guidelines below to set up the SEO metadata structure: - Keep the title tag short. Restrict the number of characters to 60. - Keep the metadata description to under 140 characters. - Use [robots metadata](https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag#xrobotstag-implementation) to tell search engines which pages to avoid and not index. By default, Googlebot will index a page and follow links to it. - Prevent content editors from using the same metadata description for multiple pages in your website - Include some help text on the SEO fields to guide content editors with the following tips: - Use a persuasive metadata description to get searchers to click your page. - Include keywords in your metadata description. - Set the metadata title tag to the H1 (title) of the page where it makes sense to do so. - Show intent in the metadata title tag. - Marketers should revise the title and meta description on pages based on the CTR for those pages. Once your structure is defined in the CMS, it's available to content editors and marketers to fill in values for fields like the title and the description. In the front end, it's then possible to retrieve the metadata to set the necessary tags for each page. #### How to set up metadata in Prepr CMS You can easily manage SEO metadata in Prepr CMS in a few steps. 1. Set up a template structure for the SEO metadata by creating an SEO component. ![SEO-component example](https://assets-site.prepr.io/1z8tvef3kukn//example-seo-component.png) > **Note:** > > In Prepr, mark which fields are the **SEO title** and the **Meta description**. > When you do this, editors get useful suggestions to improve the values they set in these fields. > Check out the [field setup guide](https://docs.prepr.io/content-modeling/field-types#item-title-and-seo-fields) for more details. 2. Include the SEO component in relevant models. These are the models that need to be rendered as pages in the front end. 3. The content editor can then store SEO metadata like the title, meta description, and set `Noindex` and `Nofollow` tags. 4. In the front end, include code that makes a [GraphQL API](https://docs.prepr.io/graphql-api) request to get the SEO metadata for the relevant page and map the values to their relevant HTML meta tags. Check out the [Google meta tags and attributes doc](https://developers.google.com/search/docs/crawling-indexing/special-tags) for attributes that are relevant to search engines. It's useful to know that many front-end frameworks abstract the creation of the meta tags and allow you to set meta tags dynamically. See the example code snippets below on how to set meta tags. **Next.js** ```ts filename="page.tsx" copy type Props = { params: { id: string } searchParams: { [key: string]: string | string[] | undefined } } { params, searchParams }: Props, parent: ResolvingMetadata ): Promise { // read route params const id = params.id // fetch data const product = await fetch(`https://.../${id}`).then((res) => res.json()) // optionally access and extend (rather than replace) parent metadata const previousImages = (await parent).openGraph?.images || [] return { title: product.title, openGraph: { images: ['/some-specific-page-image.jpg', ...previousImages], }, } } ``` **Nuxt** ```vue filename="app.vue" copy ``` ### Setting up URLs Search engines like Google use URLs as one of the factors for website ranking and to understand what the content of a page is about. SEO-friendly URLs improve indexing by search engines. Also, when visitors see a URL, they are more likely to click a URL they understand compared to a URL that has a bunch of numbers. Unlike headless CMSs, traditional CMSs include plugins that set URLs automatically. For instance, they offer built-in support for creating language-specific URLs, which is crucial for SEO. This can be easier to manage because the system automatically handles URL structures based on language settings. On the other hand, headless CMSs need custom development to manage URLs. You need to explicitly configure and maintain URL structures which is a more flexible solution but needs more technical input. #### How to set up URLs in a headless CMS Many headless CMSs allow editors to include a URL or slug for the page. The slug is the last part of the URL address that uniquely identifies a page. If possible, auto-generate this slug field value based on existing fields in the page content such as the page `name` or an article `headline`. The front end can then get this slug value to set up the URL for a page. ![slug-settings](https://assets-site.prepr.io/4qzvcxuezy52//slug-field-setup.png) To improve SEO, make sure that the generation of the slug follows the guidelines below. - Use short descriptive URLs that are easy to read like `example.com/best-practices/how-to-handle-SEO`. - Avoid complicated URLs with unrecognizable words or numbers like `example.com/products/page-id-1231/2019-09`. - Keep URLs constant. Search engines consider a new URL to be a link to a new page. If a URL changes for an existing page, the ranking of the page starts from scratch and it will take time for the page to regain its ranking in searches. - Use hyphens '-' in URLs and not underscores '\_' as separators. - Only use lowercase in URLs. - Use primary keywords in the URL, but don't repeat the keyword. Follow the guidelines below for SEO when setting up URLs in the front-end. - Organize your URLs into categories and sub-categories like `blog` or `best–practices` to improve content ranking. - Handle not-found pages and use [redirects](#managing-redirects). In the case of language-specific URLs, it's useful to note that many front-end frameworks include dynamic routing for internationalization. Check out the [Next.js docs](https://nextjs.org/docs/pages/building-your-application/routing/internationalization) for an example. ### Managing Redirects Redirects send visitors from a requested URL to another. You need redirects for pages that are broken or pages that were moved to ensure visitors and search engines access the most relevant or current page. Without the usual front-end management that you find in a traditional CMS, handling redirects can be more complex for a headless CMS. #### How to handle redirects in a headless CMS You can set up a simple content structure in the CMS to hold the original URL and the new URL for a page that needs to be redirected. In this structure, include an indicator field to define the redirect as `permanent` or `temporary`. ![Redirect model](https://assets-site.prepr.io/755unlorg9d7//example-redirect-model.png) Once the redirect structure is modeled, add a list of redirects to the CMS content. The front end can then query and send the redirect info to the deployment service. Check out the [Prepr Redirects guide](https://docs.prepr.io/development/best-practices/redirects) for example code snippets on how to handle redirects using Prepr CMS. ### Generating a sitemap A sitemap is a file that helps visitors and web crawlers navigate your website to discover and index pages. With a sitemap, you can ensure that search engines can discover your pages quickly and when a crawler finds a URL in your sitemap, it knows you want it in the search results. Traditional CMSs often include built-in support to generate a sitemap automatically. While headless CMSs don't include automatic sitemap generation for search engine crawling. Additional development effort is therefore needed for a headless setup. #### How to generate a sitemap in a headless CMS The sitemap structure should follow the guidelines below: - The XML sitemap format provides additional information about images, video, and localized versions of your pages. - The sitemap should include fully-qualified, absolute URLs. - The `` tag must be consistently updated to indicate when the content last changed. A sitemap for a project contains a list of all the pages that it wants the search engine to find like in the example below. ```xml filename="sitemap.xml" copy https://www.xmlsitemapgenerator.org/en/ 2005-01-01 monthly 0.8 ``` Headless CMSs allow you to include URL or slug info for each content page. The front end application can then retrieve this info for each page and compile the sitemap structure dynamically during deployment. #### Generate a sitemap dynamically from Prepr CMS You can make use of the available page content in Prepr CMS to generate the sitemap with the steps below. 1. Make the `base URL` available in an environment variable. 2. Query the slugs for each of your page content items. 3. Loop through these items and assign them to an array. 4. For each page in the array, set the URL in the sitemap to the `base URL` + `slug` and set the `lastmod` value to the system date field `_changed_on` of the content item. See the simple Next.js code snippet example below that automatically generates a sitemap from Prepr CMS during the build of the web app. ```js filename="sitemap.js" copy let pages = [] const { data } = await client.query({ query: gql` query { Pages { items { _slug _changed_on } } } `, }) data.Pages.items.map((page) => { pages.push({ url: process.env.SITE_URL + '/' + page._slug, lastModified: new Date(page._changed_on), changeFrequency: 'monthly', priority: 1.0, }) }) return pages } ``` > **Note:** > > In the example above, priority is set to a fixed value, but this value could also be set to a dynamic value in your content item in Prepr. For example, as one of the fields available in the SEO component of the page. ### Reviewing Content for SEO Search engines prioritize fresh and relevant content, so outdated content loses visibility in search engine results. This leads to a decline in organic traffic and a diminished online presence. When using a headless CMS, you can easily publish content updates to the front end whenever the changes are needed. However, a headless CMS usually lacks a WYSIWYG editor and SEO plugins to measure content performance. This makes it difficult to preview how content changes will look and perform before the front end goes live. #### How to review content in headless CMSs Most CMSs do not include tools to review the content for SEO impact. In these cases, you need separate monitoring software to review the content for SEO performance in search engines. #### How to review content in Prepr CMS Prepr CMS includes an [automatic SEO review feature](https://docs.prepr.io/content-management/managing-content/optimizing-content-for-seo) and the ability to include a [front end preview](https://docs.prepr.io/project-setup/setting-up-previews-and-visual-editing) for each piece of content. For more advanced SEO performance criteria, consider a dedicated marketing monitoring tool or SEO website crawler software. ## Schedule a free consultation Do you still have questions on SEO in relation to Prepr? Book a 15-minute call with a Prepr solution engineer right away. [Schedule a call](https://prepr.io/get-a-demo) Source: https://docs.prepr.io/development/best-practices/seo --- # Using TypeScript with the GraphQL API *This article explains how to make the most out of Prepr GraphQL API features when using TypeScript in your front-end app.* ## Introduction TypeScript is a strongly typed programming language that builds on JavaScript. Developers choose to use TypeScript instead of JavaScript in their projects to take advantage of cleaner and more scalable code, better code readability, type safety, and code that's easier to debug and test. By using TypeScript in *Strict Mode*, you get stronger guarantees of program correctness for types and null checks. You can use a GraphQL code generator to generate TypeScript types and keep these up to date with any schema changes. Prepr delivers strict types in the GraphQL API schema so that TypeScript in [strict mode](https://www.typescriptlang.org/tsconfig#strict) can support the following situations in the front end: - To process content fields in their expected data types. - To make sure that some fields in content items are not null before rendering the content in the front end. ## Enable strict mode and generate TypeScript types Follow the steps below to use the strict mode feature in Prepr CMS, generate TypeScript in your front end and process an API response with strong TypeScript types. > **Note:** > > The steps below are based on a [simple Next.js project](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-quick-start-guide) that connects to Prepr using Apollo Client for GraphQL API requests. Let's start with the setup in your Prepr environment. ### Enable field validation When you model content, you identify which fields should be required. So, when you set up the [*Schema*](https://docs.prepr.io/content-modeling/fundamentals) in Prepr, configure these fields as follows: 1. Click the **Schema** tab to open the *Schema Editor* and click the applicable model. 2. Click the field that needs to be set as required, choose the **Validation** tab and enable the **This field is required** toggle and click the **Save** button. ![validation](https://assets-site.prepr.io//55lphwr0reqp-typescript-required-field-setting.png) ### Set workflow stages to trigger the required validation Now that you've enabled the validation for required fields, you can define when this validation gets triggered. By default, validation rules on fields are triggered when publishing a content item. Content editors can save a content item in the *In progress* or *Review* [workflow stage](https://docs.prepr.io/content-management/collaboration) before publishing it. If you want to make sure that content editors fill the required fields at a workflow stage before *Published*, click the icon, choose the **General** option and choose the group of workflow stages for the *Required field validation stage* value. ![settings workflow stage](https://assets-site.prepr.io/6vi21uwfclpi//environment-settings-required-field-validation-stage.png) By doing this, you also make sure that required fields will not have null or undefined values in the response of your API request. ### Enable strict mode To make sure to get accurate TypeScript types from your GraphQL schema, click the icon and choose the **Access Tokens** option to view the list of access tokens. Choose the GraphQL access token with the same available workflow stages chosen above for the required validation and check the **Enable strict mode** box. ![Enable strict mode](https://assets-site.prepr.io/b1wi5xom3ck//graphql-access-token-enable-strict-mode.png) Now that the Prepr environment is ready, follow the steps below to update your front end. ### Install a GraphQL code generator Now that your Prepr environment is set up, go to your front end project and install a GraphQL code generator like [codegen](https://the-guild.dev/graphql/codegen/docs/getting-started) with the following steps: 1. Go to the terminal and execute the following commands: ```bash copy npm install graphql npm install -D typescript @graphql-codegen/cli ``` 2. Once done, initialize the setup wizard with the following command: ```bash copy npx graphql-code-generator init ``` 3. Choose the option that matches your application. For this guide, we're using a Next.js project, so we choose the `Application built with React` option. ![codegen wizard](https://assets-site.prepr.io//1q6836rp11zt-terminal-codegen-commands.png) 4. For the *Where is your schema?* question, get the API URL by logging into your Prepr account:\ a. Click the and choose the **Access tokens** option to view all the access tokens.\ b. Click one of the access tokens to open the details and copy the *API URL* value. For example, the *GraphQL Production* access token to only retrieve published content items on your site. ![access token](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) 5. For the remaining questions, choose the options as shown in the image below. ![codegen wizard](https://assets-site.prepr.io//w3c531subgs-wizard-questions.png) 5. Install additional plugins with the following command. ```bash copy npm install @graphql-codegen/client-preset @graphql-codegen/typescript @graphql-codegen/typescript-react-apollo @graphql-codegen/typescript-resolvers ``` 6. Once successfully installed, update the new file, `codegen.ts` in the root of your project as follows: ```ts filename="codegen.ts" copy {6,10-16} const config: CodegenConfig = { overwrite: true, schema: "", documents: ['!src/gql/**/*', 'src/queries/**/*.{ts,tsx}'], generates: { "src/gql/": { preset: 'client', plugins: [], presetConfig: { fragmentMasking: { unmaskFunctionName: 'getFragmentData' }, }, config: { reactApolloVersion: 3, }, }, "./graphql.schema.json": { plugins: ["introspection"] } } }; ``` ### Add GraphQL queries to your front-end application Now, that the codegen configuration is done, add your needed queries to your front end. In our Next.js example, we're creating a `GetArticles.ts` script in the `queries` folder to retrieve all articles from Prepr. ```ts filename="@/queries/GetArticles.ts" copy query GetArticles { Articles { items { _id _slug title } } } `) ``` > **Note:** > > For more information on how to connect your front-end app to Prepr to fetch content, check out [how to install Apollo Client](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-quick-start-guide#step-2-install-the-apollo-client) for examples. In the next steps, you will run the code generator to generate the TypeScript types from your queries. ### Generate TypeScript types Generate the TypeScript types by running the following command in the terminal: ```bash copy npm run codegen ``` If the command is successful you'll see output in `graphql.ts` in the new `gql` folder like in the example below. ```ts filename="@/gql/graphql.ts" copy /* eslint-disable */ /** All built-in and custom scalars, mapped to their actual values */ ID: { input: string; output: string; } String: { input: string; output: string; } Boolean: { input: boolean; output: boolean; } Int: { input: number; output: number; } Float: { input: number; output: number; } /** The DateTime type adheres to ISO 8601 standard. */ _DateTime: { input: any; output: any; } }; /** This union type holds all content models. */ __typename?: 'ApplePodcast'; _id: Scalars['String']['output']; url: Scalars['String']['output']; }; /** Single Article. */ __typename?: 'Article'; /** Count of bookmark events. */ _bookmarks?: Maybe; /** The time the content item was changed. */ _changed_on: Scalars['String']['output']; /** * Count of clicktrough events. * @deprecated Will be removed in next version, use _event instead. */ _clicktroughs?: Maybe; /** * Count of comment events. * @deprecated Will be removed in next version, use _event instead. */ _comments?: Maybe; _context?: Maybe; /** The time the content item was created. */ _created_on: Scalars['String']['output']; /** ID of your Prepr Environment. */ _environment_id: Scalars['String']['output']; /** Unique identifier for each content item. */ _id: Scalars['String']['output']; /** Count of like events. */ _likes?: Maybe; _locale: Scalars['String']['output']; _locales: Array; /** This field returns all localizations for this content item. */ _localizations: Array
; /** The time for when the content item is or will be published. */ _publish_on?: Maybe; /** * Count of purchase events. * @deprecated Will be removed in next version, use _event instead. */ _purchases?: Maybe; /** Calculated time to read in minutes. */ _read_time?: Maybe; /** * Count of share events. * @deprecated Will be removed in next version, use _event instead. */ _shares?: Maybe; /** Unique within Type, string identifier for each content item. */ _slug?: Maybe; /** Count of subscribe events. */ _subscribes?: Maybe; /** Count of view events. */ _views?: Maybe; /** * Count of vote events. * @deprecated Will be removed in next version, use _event instead. */ _votes?: Maybe; authors: Array; categories: Array; content?: Maybe>>; cover?: Maybe>>; excerpt?: Maybe; seo?: Maybe; title?: Maybe; }; /** ArticleCollection component. */ __typename?: 'ArticleCollection'; _context?: Maybe; _id: Scalars['String']['output']; articles?: Maybe>; cta_label?: Maybe; cta_url?: Maybe; description?: Maybe; heading?: Maybe; }; ... ``` > **Note:** > > `GetArticlesDocument` and `GetArticlesQuery` are generated based on the query with the name `GetArticles` defined in the previous step. > These are used to make a query request in the next step. ### Make the GraphQL request Once the types are generated and available you can make the GraphQL request to fetch the content you need to render in the front end. Fetch the content for your front-end pages like in the example `page.tsx` below. ```ts filename="page.tsx" copy async function getData() { const {data} = await getClient().query({ query: GetArticlesDocument, fetchPolicy: 'no-cache', }) if (!data) { return notFound() } return data.Articles?.items; } const articles = await getData(); return (

My blog site

    {articles?.map((article) => ( // Display a list of the fetched articles
  • {article.title}
  • ))}
); } ``` And that's it! You've successfully implemented TypeScript strict mode in your front-end. If you found this article useful and would like to explore other similar topics in the Prepr docs, [please let us know](mailto:feedback@prepr.io). Source: https://docs.prepr.io/development/best-practices/typescript --- # Integrating with webhooks *This guide shows you how to configure, implement and secure webhooks to enable real-time data sync from Prepr CMS to external applications.* ## Introduction Webhooks in Prepr CMS let you receive real-time updates for any events. Whenever a content item, asset, segment, visitor, schema or tag changes, Prepr sends a secure HTTP `POST` request to your webhook endpoint. This allows your application to respond to an event triggered in Prepr — whether that's syncing data, triggering notification or other automation, or keeping your UI in sync with content changes. ## Creating a webhook > **Note:** > > Before adding a webhook in Prepr, you need to set up your callback endpoint in your application. > The endpoint must use `HTTPS` and be able to receive `POST` requests. > Check out the [Next.js static website example](https://github.com/preprio/prepr-nextjs-static/tree/main/src/app/api) for example webhook endpoints that check for `content_item.changed` and `content_item.published` events. Webhooks in Prepr send notifications as `POST` requests and include a `JSON` payload with Prepr headers and custom headers, if defined in the webhook. Follow the steps below to add a new webhook. 1. Log in to your Prepr account with a user that has *Owner*, *Admin* or *Developer* rights. 2. Click the icon and choose the **Webhooks** option. 3. Create a new webhook by clicking the **Add webhook** button. 4. Add the URL of the endpoint where the notifications will be sent. ![Example webhook](https://assets-site.prepr.io/2m8a63xejq2//revalidate-cache-webhook-published.png) 5. Select one or more of the [supported events](#supported-events) to trigger the request. ![Example webhook - choose event](https://assets-site.prepr.io/2e718vvajpr//webhook-example-choose-event-to-trigger-webhook.png) 6. Click the **Save and close** button. Check out the list below for additional info you can include in your webhook. ### Filtering by model When you select a content item event, you will receive notifications when any content item is created, published, unpublished, changed, or deleted. You can further specify which content items you want to receive notifications from. Select one or more models to receive webhook events for content items of that specific model. ![Filter events by model](https://assets-site.prepr.io/7dwh2jcfr2l//webhook-example-choose-model-for-trigger.png) ### Custom headers There are some use cases where an application requires some fields to be added in the header of the webhook payload. For example, for access tokens. To add a custom header, enter a header key and value. ![Add custom headers](https://assets-site.prepr.io/4qcmt4tatjrr//webhook-example-headers.png) ## Supported events The events below can be used to trigger a webhook notification. Depending on your plan some options may not be available. ### Content item events | Event Name | Description | Example follow-up action | | :--- | :--- | :--- | | `content_item.published` | When a user publishes a content item. | Trigger a static website to rebuild. | | `content_item.unpublished` | When a content item is moved to *Unpublished* status. | Remove an article from a *Featured* list. | | `content_item.created` | When a new content item is created. | Send a notification to the content production manager. | | `content_item.changed` | When a content item is changed. | Notify a developer when web app config is changed and might have impact on the front end. | | `content_item.deleted` | When a content item is moved to the *Deleted items* list. | Notify a developer when system-related content items are deleted and will impact the front-end. | | `content_item.invalidated` | 1. When the remote source item used in a content item has changed in the external system. \ 2\. When an [API `PATCH`](https://docs.prepr.io/mutation-api/content-items-create-update-and-destroy#patch-a-content-item) is requested for a published content item. | Trigger the front end to clear the UI cache to track *Out of stock* products. | ### Asset events | Event Name | Description | Example follow-up action | | :--- | :--- | :--- | | `asset.created` | When an image, file or video is initially created in the *Media library*. (Not yet uploaded) | Send the asset ID to automatically link to a migrated content item. | | `asset.uploaded` | When an image, file or video is uploaded to the *Media library*. | Trigger an AI service to generate alt-text. | | `asset.changed` | When the meta-data of the image, file or video is updated like the title, description or the video thumbnail is changed. | Trigger an AI service to regenerate alt-text. | | `asset.deleted` | The image, file or video is removed from the *Media library*. | Send a notification to the content production manager. | | `storage_file.deleted` | The uploaded file is deleted from storage. | Trigger removal from your own storage. | | `cdn_file.deleted` | File is purged from the CDN. | Trigger removal from your own CDN. | ### Schema events | Event Name | Definition | Example follow-up action | | :--- | :--- | :--- | | `schema.changed` | The data model or field types changed. | Trigger TypeScript regeneration in the front end. | ### Segment events | Event Name | Definition | Example follow-up action | | :--- | :--- | :--- | | `segment.created` | When a segment is created. | Create a new corresponding target audience in your CRM. | | `segment.changed` | When a segment is changed, such as the name, description or conditions. | Sync the updated info to your CRM. | | `segment.deleted` | When a segment is removed. | Stop all automated workflows associated with this segment. | ### Visitor events | Event Name | Definition | Example follow-up action | | :--- | :--- | :--- | | `person.created` | When a new profile is added to the list of web app visitors. | Trigger a *Welcome* email to the new visitor. | | `person.changed` | When the visitor data like the email or name is modified. | Sync the updated info to your CRM. | | `person.deleted` | When a profile is deleted. | Invoke "Right to be Forgotten" protocols for GDPR compliance. | | `person.segment.added` | When the profile is added to a segment. | Trigger a campaign email the qualifying visitor. | | `person.segment.removed` | When the profile is removed from a segment. | Revoke a discount code or move them to a different list. | ### Tag events | Event Name | Definition | Example follow-up action | | :--- | :--- | :--- | | `tag.created` | When a new tag is added. | Add the tag to the global filter list in the front end. | | `tag.changed` | A tag name or property is updated. | Bulk-update the display label on all tagged items. | | `tag.deleted` | A tag is removed. | Remove the tag from the global filter list in the front end. | ## Payload examples The table below outlines the structure of the webhook request, detailing key parameters and their associated data. Each parameter represents an aspect of the event, including metadata. | Field name | Field type | Description | |-------------------------|------------|----------------------------------------------------------| | `id` | string | The event unique identifier. | | `created_on` | string | The timestamp in UTC when the event is created. | | `event` | string | The triggered event, for example `content_item.published`.| | `payload` | object | Object with info about the resource that triggered the event.| | `performed_by` | string | The Prepr user who triggered the event.| | `first_publish` | boolean | A value of `true` or `false` is filled when the event is `content_item.published`. | | `locale` | string | The language such as `en-US` is filled when the event is `content_item.changed`, `content_item.deleted`, `content_item.published`, `content_item.unpublished` or `content_item.invalidated`.| | `scope` | string | Filled when the event is `content_item.deleted`. The value is either `item` for a full content item deletion or `locale` for a partial delete.| The sections below describe the payload for each resource in more detail. ### Content item example The below example request is the payload object when a `content_item.published` event triggers for a `Post` model. ```json copy { "id": "75ec2d8c-e5ba-452e-be08-3524af2e1f49", "created_on": "2025-03-19T14:30:49+00:00", "changed_on": "2025-05-28T09:01:45+00:00", "label": "Publication", "read_time": { "en-US": 2, "nl-NL": 2 }, "publish_on": { "en-US": "2025-05-28T09:01:00+00:00", "nl-NL": "2025-02-19T12:33:00+00:00" }, "slug": { "nl-NL": "de-top-5-mythes-over-autoleasing-ontkracht", "en-US": "the-top-5-myths-about-car-leasing-debunked" }, "model": { "id": "4a8b8f49-b5cd-412f-97e0-5b04a072688d", "body_singular": "Post", "body_plural": "Posts" } } ``` | Field name | Field type | Description | |-------------------------|------------|----------------------------------------------------------| | `id` | string | The content item unique identifier. | | `created_on` | string | The timestamp in UTC when the item is created. | | `changed_on` | string | The timestamp in UTC when the item is changed. | | `label` | string | Type of resource. For content items, this value is `Publication`.| | `read-time.{locale}` |integer| Values are listed for each locale, for example `en-US`. The calculated time a user reads an article in minutes.| | `publish_on.{locale}` |string |Values are listed for each locale, for example `en-US`. The timestamp in UTC when the item is published. | | `slug.{locale}` |string|Values are listed for each locale, for example `en-US`. The slug value of the content item, if applicable.| | `model.id` |string|The internal id of the model.| | `model.body_singular` |string|The singular name of the model, for example `Post`.| | `model.body_plural` |string|The plural name of the model, for example `Posts`.| ### Asset example The below example request is the payload for an `asset.changed` event. ```json copy { "id": "c8580913-bbac-4309-9f74-95a6835930b8", "created_on": "2025-03-19T16:01:14+00:00", "changed_on": "2025-05-27T15:23:33+00:00", "label": "Photo", "name": "bmw", "body": "", "author": "", "status": null, "replaceable": true, "reference_id": "b773b7b3-97ce-40c3-ae1f-b1369d0516e1", "reference": null, "original_name": "img", "mime_type": "image/png", "extension": "png", "height": 250, "width": 496 } ``` | Field name | Field type | Description | |-------------------------|------------|----------------------------------------------------------| | `id` | string | The asset unique identifier. | | `created_on` | string | The timestamp in UTC when the asset is created. | | `changed_on` | string | The timestamp in UTC when the asset is changed. | | `label` | string | Type of resource. For assets, this value is `Photo`, `Video` or `Document`.| | `name` | string | The name of the asset. | | `body` | string | Additional information about the asset. | | `author` | string | The person who created the asset such as the photographer.| | `reference_id` | string | The external id of the asset. | ### Segments example The below example request is the payload for a `segments.deleted` event. ```json copy { "id": "sgm_5g4820qaz70", "created_on": "2025-05-28T08:52:19+00:00", "changed_on": "2025-05-28T08:52:19+00:00", "synced_on": "2025-05-28T08:52:19+00:00", "label": "Segment", "body": "New segment", "description": null, "reference_id": "new-segment", "query": { "conditions": [ { "type": "event", "event_name": "View", "result": true } ] }, "mode": 2, "count": null } ``` | Field name | Field type | Description | |-----------------------|------------|----------------------------------------------------------| | `id` | string | The segment unique identifier. | | `created_on` | string | The timestamp in UTC when the segment is created. | | `changed_on` | string | The timestamp in UTC when the segment is changed. | | `label` | string | Type of resource. For segments, this value is `Segment`.| |`body` | string | The name of the segment as it's displayed in the CMS.| |`reference_id` |string | An external id for the segment. This value can be used to identify the segment in an external CRM system, if applicable.| |`query.conditions` |array | A list of condition objects for this segment. The object fields depend on the condition options saved for this segment. For a complete list, check out the [segments doc](https://docs.prepr.io/personalization/managing-segments).| ### Visitor example The below example request is the payload for a `person.changed` event. ```json copy { "id": "3b0e2f5e-5b06-4563-828d-13ce49252672", "created_on": "2024-08-23T07:20:45+00:00", "changed_on": "2025-05-27T15:48:20+00:00", "last_seen": "2024-08-23T07:20:45+00:00", "label": "Person", "reference_id": null, "first_name": "Braylen", "last_name": "Nash", "full_name": "Braylen Nash" } ``` | Field name | Field type | Description | |--------------|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `id` | string | The visitor unique identifier. | | `created_on` | string | The timestamp in UTC when the visitor is created. | | `changed_on` | string | The timestamp in UTC when the visitor is changed. | | `label` | string | Type of resource. For visitors, this value is `Person`. | | `full-name` | string | The full name of the visitor. For a full list of available visitor characteristics, checkout the [managing visitors guide](https://docs.prepr.io/data-collection/managing-visitor-data-manually#updating-visitor-profiles). | ### Tag example The below example request is the payload for a `tag.created` event. ```json copy { "id": "c921ef1e-b8e5-4e8a-8cfa-2914665c3bf1", "created_on": "2025-09-03T12:40:49+00:00", "changed_on": "2025-09-03T12:40:49+00:00", "label": "Tag", "color": null, "body": "subscriber", "slug": "subscriber" } ``` | Field name | Field type | Description | |--------------|------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `id` | string | The visitor unique identifier. | | `created_on` | string | The timestamp in UTC when the tag is created. | | `changed_on` | string | The timestamp in UTC when the tag is changed. | | `label` | string | Type of resource. For tags, this value is `Tag`. | | `body` | string | The value of the tag. | > **Note:** > > Schema events don't contain a payload. ## Response View the full list of notifications sent to your webhook on the *Webhook detail* page in your Prepr account. From here you can view the response the Prepr server received from your endpoint. ![Webhook response](https://downloads-site.prepr.io/2ldsph3ah1vf-webhook-response.gif) Your endpoint must respond with a `2xx` status code within 12 seconds. Any other status code or a request timeout is treated as a failure and triggered for a retry. > **Note:** > > If your webhook continues to fail multiple times in 24 hours, your webhook will be disabled. > The account owner and any [*Technical contact*](https://docs.prepr.io/project-setup/managing-roles-and-permissions#technical-contact) users will be notified in this case. Check out the options below on how to handle responses with errors. ### Retry behavior Prepr attempts multiple times to deliver a given notification to your webhook endpoint with an exponential back off. You can configure a maximum number of retries. If your endpoint has been disabled you can still expect to see future retry attempts. ### Disable behaviour Prepr will actively notify the account owner and any [*Technical contact*](https://docs.prepr.io/project-setup/managing-roles-and-permissions#technical-contact) users when the webhook is misconfigured or the error rate is 100%. The email also states when the endpoint will be automatically disabled. ### View attempts To manage an existing webhook, go to **Settings → Webhooks** and click the specific webhook in the list. All attempts of this webhook of the last three days can be viewed and retried here. You can also view the response the Prepr server received from your endpoint. ### Resend webhooks You can resend a webhook event by clicking the icon. ### Disable webhooks You can easily switch off your webhook temporarily. Go to **Settings → Webhooks** and click the specific webhook in the list. Switch it off by disabling the **Active** toggle. ## Security guidelines We recommend you protect the endpoints configured in each webhook. This prevents unauthorized actions on your app. ### Authorization headers When configuring your webhook in Prepr, add an authorization *Header* with a secret access token only your incoming webhook server knows. Prepr will send all of the custom headers you added when sending a notification, including authorization. If the values don't match, you can ignore the request. ### Whitelisting Prepr server IPs You can ensure your app is always communicating with Prepr through one of our IP addresses. ```http copy 87.233.165.0/26 2001:9a8:0:4b:: 64 bits ``` ### Webhook signatures Prepr will sign the webhook events it sends to your endpoints. We do so by including a signature in each event’s `Prepr-Signature` header. This allows you to validate that the events were sent by Prepr, not by a third party. Before you can verify signatures, you need to store your endpoint’s ID when creating a new Webhook. Each ID is unique to the endpoint to which it corresponds. If you use multiple endpoints, you must obtain an ID for each one. **Verifying signatures**\ The `Prepr-Signature` header contains the full json payload you receive. Prepr generates signatures using a hash-based message authentication code (HMAC) with SHA-256. Compute an `HMAC` with the `SHA256` hash function. Use the endpoint’s signing ID as the key, and use the json payload string as the message. Compare the signature in the header to the expected signature. If a signature matches, compute the difference between the current timestamp and the received timestamp, then decide if the difference is within your tolerance. To protect against timing attacks, use a constant-time string comparison to compare the expected signature to each of the received signatures. ### Preventing replay attacks A replay attack is when an attacker intercepts a valid payload and its signature, then re-transmits them. To mitigate such attacks, Prepr includes a timestamp in the `Prepr-Signature` header. Because this timestamp is part of the signed payload, it is also verified by the signature, so an attacker cannot change the timestamp without invalidating the signature. If the signature is valid but the timestamp is too old, you can have your application reject the payload. We recommend a tolerance of five minutes between the timestamp and the current time. We advise that you use Network Time Protocol (NTP) to ensure that your server’s clock is accurate and synchronizes with the time on Prepr servers. Prepr generates the timestamp and signature each time we send an event to your endpoint. If Prepr retries an event, for example: your endpoint previously replied with a non-2xx status code, then we generate a new signature and timestamp for the next delivery attempt. Source: https://docs.prepr.io/development/best-practices/webhooks --- # Best practices for developers *Check out our offering of best practices to develop streamlined and robust code for a headless CMS.* - [Assets](https://docs.prepr.io/development/best-practices/assets): Learn how to add assets like images or video to your web app. - [CSR/SSR/SSG](https://docs.prepr.io/development/best-practices/csr-ssr-ssg): Learn about rendering strategies; CSR, SSR and SSG. - [Redirects](https://docs.prepr.io/development/best-practices/redirects): Learn how to handle redirects in a headless CMS. - [SEO](https://docs.prepr.io/development/best-practices/seo): Learn how to handle SEO in a headless CMS. - [TypeScript](https://docs.prepr.io/development/best-practices/typescript): Learn about using TypeScript with the GraphQL API. - [Using webhooks](https://docs.prepr.io/development/best-practices/webhooks): Learn about using webhooks in Prepr CMS. Source: https://docs.prepr.io/development/best-practices --- # Environment-to-environment content export *This article explains how to export content from one environment to another, for example, when you want to do system testing in your development environment with realistic content from the production environment.* ## Prerequisites Make sure that the **Allow Developer actions** and **Allow bulk update content items and assets** permissions are enabled for users who need to export content. The **Export to** action is available in the Content items list page for [*Developer*](https://docs.prepr.io/project-setup/managing-roles-and-permissions#developer) users only. ## Export content from one environment to another The process below shows you how to export content from one environment to another in the same organization. ### Update the schema in the target environment Before exporting content from one environment to another, we recommend that you trigger a [*Schema Sync*](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema) to make sure that the target schema is similar to the source schema. For example, if you want to export content from the `Production` environment to the `Development` environment, then update your `Development` schema first. This will reduce the risk of errors during the *Content Export*. Once your target schema is ready, continue with the next step to trigger a test export. ### Run the test export Follow the steps below to start the *Content Export* by running a test export. 1. First, navigate to the source environment from where you want the content exported. For example, the `Production` environment. 2. Go to the **Content** tab. 3. Hover over the content items and select the ones that you want to export. 4. If you want to select all content items, click the icon at the top of the content item list. ![Export to](https://assets-site.prepr.io/1ro1z7wo4b5d//export-to.png) 5. Click the icon at the top of the content item list to trigger the test export. 6. Select the target environment, for example, `Development`, and click the **Test export** button. ![Export content modal](https://assets-site.prepr.io/7l2gvrv432vf//export-to-pop-up-window.png) ### Fix errors If there are inconsistencies between the target and source schema such as extra validations in the target schema, or missing models, the test export will display a report with a list of descriptive errors. If errors are displayed, you can fix them directly from this pop-up window. ![Test export errors](https://assets-site.prepr.io/3ua5i9t4uo09//test-export-errors-screen.png) - Fix content items. When you click the **Update items** button, you will automatically be directed to a filtered list of the content items in the source environment which failed the validation. > **Note:** > > The **Update items** button will be disabled when more than 50 content items are in error. - Fix the target schema. - If you click the **Change validation** button, you will automatically be directed to the model in the target environment where the applicable validation rule is enabled. If the error is related to validations in a component then you will automatically be directed to the model that includes the component. - In the case of a missing model, click the **Update schema** button to add the missing model to the schema. Use the [sync schema process](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema) to ensure consistency. Once fixed, click the **Try again** button to rerun the test export. > **Note:** > > **This field is required** validation is not triggered during the export. ### Export the content When the test export is completed without any errors, click the **Export items** button to run the actual export. ![Test export complete](https://assets-site.prepr.io/6ndwwx29ek64//export-test-successful.png) Once done, navigate to the target environment to find the exported content items. - If the target environment was completely empty then the source content items and related assets are simply copied to the target environment. - If content items already existed in the target environment the export finds matching content items and overwrites these. Matching content items have the same *Content item ID* value, that is, content items that were previously synced. - Only assets used in content items are synced. > **Note:** > > Make sure that users are not updating content items in the target environment during the actual content export. Source: https://docs.prepr.io/development/working-with-cicd/syncing-content --- # Sync schemas between two environments *This guide shows you how to update one schema with the other, for example, when you have a development and production environment with the same schema that is out of sync.* ## Introduction In the event that you have separate environments, for example, *Development*, *Testing*, *Acceptance*, and *Production*, you may need to sync the schemas in these environments. You can do this automatically by running a schema sync in Prepr. ![Schema sync modal](https://assets-site.prepr.io/vgct29ttqc1//sync-schema-options.png) There are a few schema sync processes you can choose from: - [*GitHub schema sync*](#github-schema-sync) - Export to a GitHub repository or import from a GitHub repository to keep your schema in sync between environments. - [*GitLab schema sync*](#gitlab-schema-sync) - Export to a GitLab project or import from a GitLab project to keep your schema in sync between environments. - [*Bitbucket schema sync*](#bitbucket-schema-sync) - Export to a Bitbucket repository or import from a Bitbucket repository to keep your schema in sync between environments. - [*Azure DevOps schema sync*](#azure-devops-schema-sync) - Export to a Azure DevOps repository or import from a Azure DevOps repository to keep your schema in sync between environments. - [*Direct schema sync*](#direct-schema-sync) - Use this process to compare and sync your schema directly in Prepr between two environments in the same organization. ## Use cases See the following use cases for when you may need to synchronize your schema: - **Sync production to development** An example *DTAP strategy* is to maintain the schema in the *Production* environment and when the same content structure is needed for realistic test cases this schema needs to be copied to the *Development*, *Test*, or *Acceptance* environment. Learn more on [how to set up a DTAP configuration](https://docs.prepr.io/project-setup/setting-up-environments#set-up-a-dtap-configuration). - **Sync development to production** After testing your development on the latest schema version in the *Development* or *Test* environments, it's possible that you may need to make changes to the live schema. In this case, you could copy these changes to the *Production* environment. - **Share a schema between environments** Use the *Shared schema* feature to maintain one schema across multiple environments, for example, for multiple brands. Check out more details in the [environments doc](https://docs.prepr.io/project-setup/setting-up-environments#environment-setup-for-multiple-brands). A shared schema can also be imported from GitHub or GitLab or exported to GitHub or GitLab. This could be useful for agencies when you want to do testing with client's shared schema in your own organization instead of in the client organization. ## Prerequisites You need to have a role, such as the *Developer* or *Admin* role, with the **Schema → Write** permission enabled to have access to the **Sync schema** action. If you need to sync a shared schema, then you need the organization **Schema → Write** permission enabled. Check out [roles and permissions in Prepr](https://docs.prepr.io/project-setup/managing-roles-and-permissions#add-or-edit-roles) for more details. ## GitHub schema sync The process below shows you how to connect to your GitHub account and sync a schema using a GitHub repository. ### Generate a personal access token To use a GitHub repository to sync schemas, follow the steps below to create a personal access token in your GitHub account. 1. Log in to your GitHub account and follow the [GitHub docs](https://docs.github.com/en/repositories/creating-and-managing-repositories/quickstart-for-repositories) to create a repository and necessary branches to manage your Prepr schema. 2. In your GitHub account, click your profile photo to expand the sidebar menu on the right, and click the **Settings** link. 3. From the side menu on the left, go to **Developer Settings** at the bottom. 4. Expand the **Personal access tokens** option and click the [**Fine-grained tokens**](https://github.com/settings/tokens?type=beta) link. 5. Click the **Generate new token** button, enter a *Token name* (Max. 40 characters) to indicate the purpose of the token like `Prepr schema sync` and select an *Expiration* for the token. 6. Under the *Repository access* section, choose the **Only select repositories** option and choose the repository you created earlier. 7. Under the *Permissions* section, click the **+ Add permissions** button, scroll down and select the **Contents** option. Make sure to select an access level of **Read and write**. 8. Click the **Generate token** button and make sure to copy the generated token and save it somewhere safe. The token value will only be visible once. Once done, you are ready to connect to your GitHub repository from Prepr CMS. ### Connect to GitHub The next step is to connect the Prepr CMS environment or organization to your GitHub account with the steps below. > **Note:** > > You can enable the *GitHub sync* for each environment by either activating the *GitHub* option on the **Integrations** page or via the **Schema** page as detailed below. 1. Navigate to each Prepr environment or organization where schemas need to be synced. 2. At the top of the **Schema** page in your environment or at the top of the **Shared schema** page in the organization, click the icon and choose the **Sync schema** option. ![Schema page - Enable GitHub sync](https://assets-site.prepr.io/2c9sxyynvhhj//github-sync-option.png) 3. Enter the fine-grained personal access token you copied in [the previous step](#generate-a-personal-access-token) and click the **Continue** button. ![GitHub personal access token](https://assets-site.prepr.io/5rv4jxe29ps4//github-schema-sync-enter-access-token.png) 4. Choose the repository you want to use for your Prepr schema. You can choose to **Use a custom repository path** and enter your preferred folder structure for the location of the synced schema or keep the default value of `prepr/schema` and click the **Continue** button. ![Repository and path](https://assets-site.prepr.io/7h0yvfsqsbev//github-setup-simplified.png) 5. Choose the **Branch** for the schema sync and click the **Continue** button. 6. Select your preferred sync method for this environment and click the **Complete** button. ![Choose export or import](https://assets-site.prepr.io/786hizxhxsm2//choose-github-setup.png) - Choose **Export only** if this environment has the most up-to-date schema. - Choose **Import only** if the schema in this environment needs to be updated with a more up-to-date schema. - Choose **Export & Import** for the situation where you have schema updates from multiple development or testing environments. Once GitHub is connected, you'll see the message below. ![GitHub successfully installed](https://assets-site.prepr.io/5w309ck32eka//github-installation-successful.png) You can then sync your schema with the GitHub repository you selected in the setup. ### Export to GitHub If your environment or organization has the most up-to-date schema, you can export it to a GitHub repository to make it available to other environments. 1. Once your GitHub account is connected, simply click the **Sync schema** option. Choose the branch you want to export to and click the **Export** button. ![Sync schema with GitHub example](https://assets-site.prepr.io/3u9nfgqrzroi//export-to-github.png) When the export starts, you'll see the **Export in progress** status. ![Export in progress](https://assets-site.prepr.io/6y35x9c4dqqs//export-in-progress.png) 2. Once completed, the repository will be updated as follows: - If the repository is empty, the push request simply creates JSON files for each of the models, components, enumerations and remote sources in the schema. - The model JSON file names will match each model's *Singular name* in lowercase. - The component JSON file names will match their *Type Name* values in lower case. - The enumeration JSON file names will match their *Type Name* values in lower case. - The remote source JSON file names will match their *Type Name* values in lower case. - If the repository already has JSON files, expect the following behavior: - If the JSON files match models, components, enumerations, and remote sources in the schema, these JSON files will be overwritten. - If there are JSON files in the repository that don't have matching models, components, enumerations, or remote sources, these JSON files will be removed from the repository. - For models, components, enumerations, and remote sources that don't have matching JSON files, new JSON files will be created in the repository. When successful, you'll see an **Export complete** status message. ![GitHub export complet](https://assets-site.prepr.io/487xbimr0843//export-complete.png) Now your GitHub repository is up to date with the latest schema. And you can use this repository to sync any other schema in your organization by triggering an [import from GitHub](#import-from-github). ### Import from GitHub If your environment or organization needs to be updated with a more up-to-date schema, you can do an import from a GitHub repository to update it. 1. Once your GitHub account is connected, simply click the **Sync schema** option. Choose the branch and click the **Import** button to trigger the **Validating GitHub schema** process. ![Validating GitHub schema](https://assets-site.prepr.io/6j20l1abppko//validating-github-schema.png) 2. When the schema validation completes, you can preview your changes. ![import preview changes](https://assets-site.prepr.io/4ugkdf57k9o1//github-import-preview-changes.png) 3. When you're happy with the changes, click the **Import schema** button. 4. Once the import is completed, the schema is updated as follows: - Models, components, enumerations, and remote sources will be overwritten with the matching JSON files in the GitHub repository. The pull request will find matching models, components, enumerations, and remote sources using the model *Singular name* and the component and remote source *Type name* values in lower case. - If there are JSON files that don't have a match in your environment or organization schema, new models, components, enumerations, and remote sources will be created for each of these. - If there are models, components, enumerations and remote sources that don't have a matching JSON file, these will be automatically deleted if they are not in use by any content items. That's it! You‘ve synchronized the schema using a GitHub repository. > **Note:** > > You can disconnect a GitHub account from a Prepr environment. For example, if you need to switch to a different GitHub account. > In this case, simply click go to the **Settings → Integrations** page and click the **Deactivate** link for the GitHub integration. ## GitLab schema sync The process below shows you how to connect to your GitLab account and sync a schema using a GitLab repository. ### Generate a project access token To use a GitLab project to sync schemas, follow the steps below to create a project access token in your GitLab account. 1. Log in to your GitLab account and follow the [GitLab docs](https://docs.gitlab.com/ee/user/project/index.html) to create a project to manage your Prepr schema. 2. While in your project, expand the **Settings** in the side menu. 3. Click the **Access Tokens** link and in the **Project Access Tokens** page, click the **Add new token** button. 4. Enter a *Token name* to indicate the purpose of the token like `Prepr schema sync` and select an *Expiration* for the token. Choose a role for the users who will be syncing schemas, for example: `Maintainer`. 5. In the *Select scopes* section, check the **api**, **read\_repository** and the **write\_repository** options. 6. Click the **Create project access token** button and make sure to copy the generated token and save it somewhere safe. The token value will only be visible once. 7. To define the push and merge permissions for this project, expand the **Settings** in the side menu and click the **Repository** link. Expand the **Protected branches** section to define and set the role for the **Allow to merge** and the **Allow to push and merge** to the same role that you chose for the access token. 8. Get and save the *Project ID* value of your GitLab project as follows: - In your project, expand the **Settings** in the side menu and click the **General** link. Copy and save the **Project ID** value. Once done, you are ready to connect to your GitLab account from Prepr CMS. ### Connect to GitLab The next step is to connect the Prepr CMS environment or organization to your GitLab account with the steps below. > **Note:** > > You can enable the *GitLab sync* for each environment by either activating the *GitLab* option on the **Integrations** page or via the **Schema** page as detailed below. 1. Navigate to each Prepr environment or organization where schemas need to be synced. 2. At the top of the **Schema** page in your environment or at the top of the **Shared schema** page in the organization, click the icon and choose the **Sync schema** option. ![Enable GitLab sync](https://assets-site.prepr.io/2dd15jao1jk7//gitlab-sync-option.png) Click the **GitLab sync** option. 3. In the setup flow, paste the **Project ID**, and the **Access token** that you saved in the [previous step](#generate-a-project-access-token). You can choose to **Use a custom repository path** and enter your preferred folder structure for the location of the synced schema or keep the default value of `prepr/schema` and click the **Continue** button. ![GitLab form in Prepr](https://assets-site.prepr.io/6ct3ezypzzi3//gitlab-setup-simplified.png) 4. Choose the *Branch* and click the **Continue** button to see the sync options. 5. Select your preferred sync method for this environment and click the **Complete** button. - Choose **Export only** if this environment has the most up-to-date schema. - Choose **Import only** if the schema in this environment needs to be updated with a more up-to-date schema. - Choose **Export & Import** for the situation where you have schema updates from multiple development or testing environments. Once GitLab is connected, you'll see the message below. ![GitLab installation successful](https://assets-site.prepr.io/3psojp80uocd//gitlab-installation-successful.png) ### Export to GitLab 1. Once your GitLab project is connected, simply click the **Sync schema** option. Choose the branch you want to export to and click the **Export** button. ![GitLab export](https://assets-site.prepr.io/yfs2v2ybyqf//export-to-gitlab.png) 2. When the export starts, you'll see the **Export in progress** status. ![Export in progress](https://assets-site.prepr.io/6y35x9c4dqqs//export-in-progress.png) 3. Once completed, the repository will be updated as follows: - If the repository is empty, the push request simply creates JSON files for each of the models, components, enumerations and remote sources in the schema. - The model JSON file names will match each model's *Singular name* in lowercase. - The component JSON file names will match their *Type Name* values in lower case. - The enumeration JSON file names will match their *Type Name* values in lower case. - The remote source JSON file names will match their *Type Name* values in lower case. - If the repository already has JSON files, expect the following behavior: - If the JSON files match models, components, enumerations, and remote sources in the schema, these JSON files will be overwritten. - If there are JSON files in the repository that don't have matching models, components, enumerations, or remote sources, these JSON files will be removed from the repository. - For models, components, enumerations, and remote sources that don't have matching JSON files, new JSON files will be created in the repository. When successful, you'll see an **Export complete** status message. Now your GitLab repository is up to date with the latest schema. And you can use this repository to sync any other schema in your organization by triggering an import from GitLab. ### Import from GitLab 1. If your GitLab has previously been connected, simply click the **Sync schema** option. Choose the branch and click the **Import** button to trigger the **Validating GitLab schema** process. ![Validating GitLab schema](https://assets-site.prepr.io/402duiwe2iss//validating-gitlab-schema.png) 2. When the schema validation completes, you can preview your changes. ![import preview changes](https://assets-site.prepr.io/4ugkdf57k9o1//github-import-preview-changes.png) 3. When you're happy with the changes, click the **Import schema** button. Once the import is completed, the schema is updated as follows: - Models, components, enumerations, and remote sources will be overwritten with the matching JSON files in the GitLab repository. The pull request will find matching models, components, enumerations, and remote sources using the model *Singular name* and the component and remote source *Type name* values in lower case. - If there are JSON files that don't have a match in your environment or organization schema, new models, components, enumerations, and remote sources will be created for each of these. - If there are models, components, enumerations and remote sources that don't have a matching JSON file, these will be automatically deleted if they are not in use by any content items. That's it! You‘ve synchronized the schema using a GitLab repository. > **Note:** > > You can disconnect a GitLab account from a Prepr environment. For example, if you need to switch to a different GitLab account. > In this case, simply click go to the **Settings → Integrations** page and click the **Deactivate** link for the GitLab integration. ## Bitbucket schema sync The process below shows you how to connect to your Bitbucket account and sync a schema using a Bitbucket cloud repository. ### Generate a repository access token To use a Bitbucket repository to sync schemas, follow the steps below to create a repository access token in your Bitbucket account. 1. Log in to your Bitbucket account and follow the [Bitbucket docs](https://support.atlassian.com/bitbucket-cloud/docs/create-a-git-repository/) to create a repository to manage your Prepr schema. 2. While in your repository, click the **Repository settings** in the left side menu. 3. Click the **Access Tokens** link and in the **Access Tokens** page, click the **Create Repository Access Token** button. 4. Enter a *Name* to indicate the purpose of the token like `Prepr schema sync`. 5. In the *Repositories* section check the **Read** and **Write** options. 6. Click the **Create** button and make sure to copy the generated token and save it somewhere safe. The token value will only be visible once. Once done, you are ready to connect to your Bibucket account from Prepr CMS. ### Connect to Bitbucket The next step is to connect the Prepr CMS environment or organization to your Bitbucket account with the steps below. > **Note:** > > You can enable the *Bitbucket sync* for each environment by either activating the *Bitbucket* option on the **Integrations** page or via the **Schema** page as detailed below. 1. Navigate to each Prepr environment or organization where schemas need to be synced. 2. At the top of the **Schema** page in your environment or at the top of the **Shared schema** page in the organization, click the icon and choose the **Sync schema** option. ![Schema page - Enable Bitbucket sync](https://assets-site.prepr.io/1pwtx98ns6yv//bitbucket-sync-option.png) 3. Click the **Bitbucket sync** option and paste the *Bitbucket workspace* name, *Bitbucket Repository* name, and the *Bitbucket access token* that you saved in the [previous step](#generate-a-repository-access-token). You can choose to **Use a custom repository path** and enter your preferred folder structure for the location of the synced schema or keep the default value of `prepr/schema` and click the **Continue** button. ![Bitbucket form in Prepr](https://assets-site.prepr.io/3frhibo9cf90//bitbucket-setup-simplified.png) 4. Choose the *Branch* and click the **Continue** button to see the sync options. 5. Select your preferred sync method for this environment and click the **Complete** button. - Choose **Export only** if this environment has the most up-to-date schema. - Choose **Import only** if the schema in this environment needs to be updated with a more up-to-date schema. - Choose **Export & Import** for the situation where you have schema updates from multiple development or testing environments. Once Bitbucket is connected, you'll see the message below. ![Bitbucket installation successul](https://assets-site.prepr.io/5ewb4022qaab//bitbucket-installation-successful.png) ### Export to Bitbucket 1. Once your Bitbucket repository is connected, simply click the **Sync schema** option. Choose the branch you want to export to and click the **Export** button. ![Bitbucket export](https://assets-site.prepr.io/77oatju4lq21//bitbucket-sync-export.png) 2. When the export starts, you'll see the **Export in progress** status. ![Export in progress](https://assets-site.prepr.io/6y35x9c4dqqs//export-in-progress.png) 3. Once completed, the repository will be updated as follows: - If the repository is empty, the push request simply creates JSON files for each of the models, components, enumerations and remote sources in the schema. - The model JSON file names will match each model's *Singular name* in lowercase. - The component JSON file names will match their *Type Name* values in lower case. - The enumeration JSON file names will match their *Type Name* values in lower case. - The remote source JSON file names will match their *Type Name* values in lower case. - If the repository already has JSON files, expect the following behavior: - If the JSON files match models, components, enumerations, and remote sources in the schema, these JSON files will be overwritten. - If there are JSON files in the repository that don't have matching models, components, enumerations, or remote sources, these JSON files will be removed from the repository. - For models, components, enumerations, and remote sources that don't have matching JSON files, new JSON files will be created in the repository. When successful, you'll see an **Export complete** status message. Now your Bitbucket repository is up to date with the latest schema. And you can use this repository to sync any other schema in your organization by triggering an import from Bitbucket. ### Import from Bitbucket 1. If your Bitbucket repository has previously been connected, simply click the **Sync schema** option. Choose the branch and click the **Import** button to trigger the **Validating Bitbucket schema** process. ![validating bitbucket schema](https://assets-site.prepr.io/4mjdhp8fx6hw//validating-bitbucket-schema.png) 2. When the schema validation completes, you can preview your changes. ![import preview changes](https://assets-site.prepr.io/4ugkdf57k9o1//github-import-preview-changes.png) 3. When you're happy with the changes, click the **Import schema** button. Once the import is completed, the schema is updated as follows: - Models, components, enumerations, and remote sources will be overwritten with the matching JSON files in the Bitbucket repository. The pull request will find matching models, components, enumerations, and remote sources using the model *Singular name* and the component and remote source *Type name* values in lower case. - If there are JSON files that don't have a match in your environment or organization schema, new models, components, enumerations, and remote sources will be created for each of these. - If there are models, components, enumerations and remote sources that don't have a matching JSON file, these will be automatically deleted if they are not in use by any content items. That's it! You‘ve synchronized the schema using a Bitbucket repository. > **Note:** > > You can disconnect the *Bitbucket* account from your Prepr environment. For example, if you need to switch to a different *Bitbucket* account. > In this case, simply click go to the **Settings → Integrations** page and click the **Deactivate** link in the Bitbucket integration block. ## Azure DevOps schema sync The process below shows you how to connect to your Azure DevOps account and sync a schema using an Azure DevOps repository. ### Generate a personal access token in Azure DevOps Follow the instructions in the [Microsoft docs](https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/use-personal-access-tokens-to-authenticate?toc=%2Fazure%2Fdevops%2Forganizations%2Ftoc.json\&view=azure-devops\&tabs=Windows#create-a-pat) to create a personal access token. Once you've noted the names of your *Azure DevOps* organization, project, repository, and copied the generated access token, you're ready to connect to your Azure DevOps account from Prepr CMS. ### Connect to Azure DevOps The next step is to connect the Prepr CMS environment or organization to your Azure DevOps account with the steps below. > **Note:** > > You can enable the *Azure DevOps sync* for each environment by either activating the *Azure DevOps* option on the **Integrations** page or via the **Schema** page as detailed below. 1. Navigate to each Prepr environment or organization where schemas need to be synced. 2. At the top of the **Schema** page in your environment or at the top of the **Shared schema** page in the organization, click the icon and choose the **Sync schema** option. ![Schema page - Enable Azure DevOps](https://assets-site.prepr.io/2c4m4yi6m0cn//azure-sync-option.png) 3. Click the **Azure DevOps sync** option and enter values for the *Azure DevOps organization*, *Azure DevOps project*, *Azure DevOps repository* and paste the *Azure DevOps access token*. Then, click the **Continue** button. You can choose to **Use a custom repository path** and enter your preferred folder structure for the location of the synced schema or keep the default value of `prepr/schema` and click the **Continue** button. ![Azure DevOps form in Prepr](https://assets-site.prepr.io/1zpfv3j8ohi4//azure-devops-setup-simplified.png) 4. Choose the *Branch* and click the **Continue** button to see the sync options. 5. Select your preferred sync method for this environment and click the **Complete** button. - Choose **Export only** if this environment has the most up-to-date schema. - Choose **Import only** if the schema in this environment needs to be updated with a more up-to-date schema. - Choose **Export & Import** for the situation where you have schema updates from multiple development or testing environments. Once Azure DevOps is connected, you'll see the **Installation successful** message. ### Export to Azure DevOps 1. Once your *Azure DevOps* repository is connected, simply click the **Sync schema** option. Choose the branch you want to export to and click the **Export** button. 2. When the export starts, you'll see the **Export in progress** status. ![Export in progress](https://assets-site.prepr.io/6y35x9c4dqqs//export-in-progress.png) 3. Once completed, the repository will be updated as follows: - If the repository is empty, the push request simply creates JSON files for each of the models, components, enumerations and remote sources in the schema. - The model JSON file names will match each model's *Singular name* in lowercase. - The component JSON file names will match their *Type Name* values in lower case. - The enumeration JSON file names will match their *Type Name* values in lower case. - The remote source JSON file names will match their *Type Name* values in lower case. - If the repository already has JSON files, expect the following behavior: - If the JSON files match models, components, enumerations, and remote sources in the schema, these JSON files will be overwritten. - If there are JSON files in the repository that don't have matching models, components, enumerations, or remote sources, these JSON files will be removed from the repository. - For models, components, enumerations, and remote sources that don't have matching JSON files, new JSON files will be created in the repository. When successful, you'll see an **Export complete** status message. Now your Azure DevOps repository is up to date with the latest schema. And you can use this repository to sync any other schema in your organization by triggering an import from Azure DevOps. ### Import from Azure DevOps 1. Once your *Azure DevOps* repository is connected, simply click the **Sync schema** option. Choose the branch and click the **Import** button to trigger the **Validating Azure DevOps schema** process. 2. When the schema validation completes, you can preview your changes. ![import preview changes](https://assets-site.prepr.io/4ugkdf57k9o1//github-import-preview-changes.png) 3. When you're happy with the changes, click the **Import schema** button. Once the import is completed, the schema is updated as follows: - Models, components, enumerations, and remote sources will be overwritten with the matching JSON files in the Azure DevOps repository. The pull request will find matching models, components, enumerations, and remote sources using the model *Singular name* and the component and remote source *Type name* values in lower case. - If there are JSON files that don't have a match in your environment or organization schema, new models, components, enumerations, and remote sources will be created for each of these. - If there are models, components, enumerations and remote sources that don't have a matching JSON file, these will be automatically deleted if they are not in use by any content items. That's it! You‘ve synchronized the schema using a *Azure DevOps* repository. > **Note:** > > You can disconnect the Azure DevOps account from your Prepr environment. For example, if you need to switch to a different Azure DevOps account. > In this case, simply go to the **Settings → Integrations** page and click the **Deactivate** link in the Azure DevOps integration block. ## Direct schema sync Use the direct schema sync process if you choose not to use an external service like [*GitHub sync*](#github-schema-sync), [*GitLab sync*](#gitlab-schema-sync), [*Bitbucket sync*](#bitbucket-schema-sync), or [*Azure DevOps*](#azure-devops-schema-sync). The direct schema sync needs no external tools, whereas using an external service gives you more flexibility and the added benefit of version control. Note the list of constraints below before triggering the direct schema sync process. ### Constraints - This process only works between environments in the same organization. - Models in your target environment will be overwritten with the models from the current environment if their *Singular names* match. Fields will be added or deleted to match the updated structure. > **Note:** > > Even if a model has the same *Name*, but different *Singular name* in the current and target environments, it's treated as a new model for the target environment. To ensure models are matched and updated successfully make sure the *Singular name* matches. - Components, enumerations and remote sources in your target environment will be overwritten with the components, enumerations and remote sources from the current environment if their *Type name* values match. Fields will be added or deleted to match the updated structure. > **Note:** > > Even if a component, enumeration or remote source has the same *Name*, but different *Type name* in the current and target environments, it's treated as a new component, enumeration, or remote source for the target environment. To ensure they are matched and updated successfully make sure the *Type name* matches. - Models, components, enumerations or remote sources that are not in the current environment, but present in the target environment will be deleted if they are unused. > **Warning:** > > Once the sync has finished successfully, the changes cannot be undone. Make sure to preview the changes carefully for any discrepancies before confirming the schema sync. The process below shows you how to sync your schema from one environment (*current environment*) to another (*target environment*). ### Choose the source schema First, decide on the source schema and navigate to the environment in which it was created. This will be your *current environment*. For example, if you want to copy your *Development* schema to *Production*, navigate to the *Development* environment. ### Run the schema sync process Once you’ve opened the chosen environment with the source schema, do as follows: 1. At the top of the **Schema** page, click the icon and choose the **Sync schema** option. ![Schema sync modal](https://assets-site.prepr.io/2zacx4huwchz//direct-sync-option.png) 2. Choose the **Direct sync** option to start the process. ![Direct schema sync](https://assets-site.prepr.io/2whpiuiv6b93//direct-sync-conditons.png) > **Note:** > > Remote source URLs, headers and images domains will be set when the destination values are blank, otherwise they will remain untouched. 3. Review the conditions before checking the **I understand, continue** checkbox. Then, select the *Target environment* you want to sync your schema to. Here you can only choose an environment in the same organization. 4. Click the **Preview changes** button to review the upcoming changes. ![Sync schema preview](https://assets-site.prepr.io/zrdxzf2cedr//direct-sync-preview-changes.png) 5. To see specific differences in JSON, expand the model, component, enumeration or remote source and click an item in the list. 6. When you're happy with the upcoming changes, click the **Sync schema** button to start the sync process. ### Check the result After the sync is done, you'll get a **Sync completed** status message. Click the **See sync overview** link to open an overview of the changes made. You can then confirm the models, components, enumerations and remote sources that were added, updated or deleted by navigating to your target environment. That's it! You‘ve successfully synchronized the schema from your current environment to the target environment. If you have any questions about schema synchronization, please [reach out to our Support team](https://prepr.io/support). Source: https://docs.prepr.io/development/working-with-cicd/syncing-a-schema --- # Validating Prepr schema JSON files *This guide shows you how to validate schema JSON files using a GitHub validation action and the Prepr schema spec.* ## Introduction In the event that you want to maintain your Prepr schema through a code editor, you can validate these schema JSON files before performing a [schema import](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema#import-from-github). > **Note:** > > This guide is based on using *GitHub* as your schema repository. > Similar steps can be applied to other platforms like *GitLab*, *BitBucket* or *Azure DevOps*, but you might need to create the corresponding validation action or pipeline instead of simply copying the validation `YML` we provide. ## Validating your schema using the Prepr schema spec Follow the steps below to [validate schema JSON files](https://github.com/preprio/action-schema-validation/tree/main) against the [Prepr schema spec](https://github.com/preprio/action-schema-validation/blob/main/spec/2026-03-05.json5). ### Create your GitHub schema repo - If you don't yet have one, create a [GitHub repo](https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-new-repository) to store the source for your Prepr schema. - Make sure to upload the schema JSON files into a `/prepr/schema` folder structure in your "staging" branch. ### Install the Prepr schema validation - From your `main` branch, create the workflow action `.github/workflows/prepr-schema-validation.yml` in your repository and copy the following code to this file: ```yaml copy name: Validate Prepr schema on: workflow_dispatch: pull_request: paths: - 'prepr/schema/*.json' push: branches: - main paths: - 'prepr/schema/*.json' jobs: validate-prepr-schema: uses: preprio/action-schema-validation/.github/workflows/prepr-schema-validation.yml@v1.0.20 ``` - Once the workflow is successfully created and active, push your JSON files to the `main` branch to trigger it. ### Import the valid schema When there are no errors detected from the validation workflow, you can then [import the schema](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema#import-from-github) into your desired Prepr environment. Check out the [Syncing a schema guide](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema) for more details on importing a schema using different platforms. That's it! You‘ve successfully validated the schema from your current environment to the target environment. If you have any questions or encounter issues with the [schema validation](https://github.com/preprio/action-schema-validation/tree/main), please [reach out to our Support team](https://prepr.io/support). Source: https://docs.prepr.io/development/working-with-cicd/validating-a-schema --- # Manually exporting and importing a schema *This guide shows you how to manually export and import parts of a schema, namely, models, components, enumerations and remote sources.* ## Introduction In the event that you have separate environments and you want to copy parts of the schema from one environment to another, you can export and import the parts you need, namely, [a model](#export-and-import-a-model), [a component](#export-and-import-a-component), [an enumeration](#export-and-import-an-enumeration), or [a remote source](#export-and-import-a-remote-source). > **Note:** > > To copy an entire schema from one environment to another, it's much easier to [sync a schema automatically](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema) using one of the sync options. ## Export and import a model Use the export and import if you only need a couple of models copied from one environment to another. For example, export a model from your staging environment and import the model into your production environment. To sync a schema with models, components, enumerations and remote sources from another environment then follow the process detailed in the [Sync schema doc](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema) instead. To share the same models across multiple environments, for example when an organization has different brands, but needs content in separate environments, you can create a shared schema as detailed in the [Shared schemas doc](https://docs.prepr.io/project-setup/architecture-scenarios/shared-schema). To export a model, follow these steps: 1. Click the **Schema** tab to open the *Schema Editor*. 2. Click a model from the list of models on the left. 3. Click the button at the top of the model. 4. Click **Export model** to download a JSON file of the model. ![export-model](https://downloads-site.prepr.io/i7b2iwb48nr-export-model.gif) To import a model, follow these steps: 1. Click the **Schema** tab to open the *Schema Editor*. 2. Then, click the **+ Add model** button. 3. Click **Or import a model**. 4. Choose the JSON file of the model that you want to import. When you import a model, the settings are also copied across. ![import-model](https://downloads-site.prepr.io/42d2oussyg6m-import-model.gif) ## Export and import a component Use the export and import if you only need a couple of components copied from one environment to another. For example, export a component from your staging environment and import the component into your production environment. To sync a schema with models, components, enumerations and remote sources from another environment then follow the process detailed in the [Sync schema doc](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema) instead. To share the same components across multiple environments, for example when an organization has different brands, but needs content in separate environments, you can create a shared schema as detailed in the [Shared schemas doc](https://docs.prepr.io/project-setup/architecture-scenarios/shared-schema). To export a component, follow these steps: 1. Click the **Schema** tab to open the **Schema Editor**. 2. Click the component that you want to export from the list of components on the left. 3. Click the button at the top of the component. 4. Click **Export component** to download a JSON file of the component. ![export-component](https://downloads-site.prepr.io/6ckxk6ka60w2-export-a-component.gif) To import a component, follow these steps: 1. Click the **Schema** tab to open the **Schema Editor**. 2. Then, click the **+ Add component** button. 3. Click **Or import a component**. 4. Choose the JSON file of the component that you want to import. ![import-component](https://downloads-site.prepr.io/2o4wga7ya4pr-import-a-component.gif) ## Export and import an enumeration Use the export and import if you only need a couple of enumerations copied from one environment to another. For example, export an enumeration from your production environment and import it into your development environment. To sync a schema with models, components, enumerations and remote sources from another environment then follow the process detailed in the [Sync schema doc](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema) instead. To share the same enumerations across multiple environments, for example when an organization has different brands, but needs content in separate environments, you can create a shared schema as detailed in the [Shared schemas doc](https://docs.prepr.io/project-setup/architecture-scenarios/shared-schema). To export an enumeration, follow these steps: 1. Click the **Schema** tab to open the **Schema Editor**. 2. Click the enumeration that you want to export from the list of enumerations on the left. 3. Click the button at the top of the enumeration. 4. Click the **Export enumeration** option to download a JSON file of the enumeration. ![export enumeration](https://assets-site.prepr.io//6mk9yi50hg1o-export-enumeration.png) When the export is successful, you'll find the JSON file in the location you selected. To import an enumeration, follow these steps: 1. Click the **Schema** tab to open the **Schema Editor**. 2. Then, click the **+ Add enumeration** button. 3. Click the **Import enumeration** link. 4. Choose the JSON file of the enumeration that you want to import. ![import enumeration](https://assets-site.prepr.io//5skbn5qa64nb-import-enumeration.png) When the import is successful, you'll see the detailed enumeration in your schema. ## Export and import a remote source Use the export and import if you only need a couple of remote sources copied from one environment to another. For example, export a remote source from your production environment and import it into your development environment. To sync a schema with models, components, enumerations and remote sources from another environment then follow the process detailed in the [Sync schema doc](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema) instead. To share the same remote sources across multiple environments, for example when an organization has different brands, but needs content in separate environments, you can create a shared schema as detailed in the [Shared schemas doc](https://docs.prepr.io/project-setup/architecture-scenarios/shared-schema). To export a remote source, follow these steps: 1. Click the **Schema** tab to open the **Schema Editor**. 2. Click the remote source that you want to export from the list of remote sources on the left. 3. Click the button at the top of the remote source. 4. Click the **Export remote source** option to download a JSON file of the remote source. ![export custom source](https://assets-site.prepr.io//6231p0s82264-export-custom-source.png) When the export is successful, you'll find the JSON file in the location you selected. To import a remote source, follow these steps: 1. Click the **Schema** tab to open the **Schema Editor**. 2. Then, click the **+ Add remote source** button. 3. Click the **Import custom source** link. 4. Choose the JSON file of the remote source that you want to import. When the import is successful, you'll see the detailed remote source in your schema. If you have any questions about exporting and importing parts of your schema, please [reach out to our Support team](https://prepr.io/support). Source: https://docs.prepr.io/development/working-with-cicd/exporting-and-importing-a-schema --- # Working with CI/CD Discover advanced Prepr CMS features designed to support your CI/CD processes. - [Syncing content](https://docs.prepr.io/development/working-with-cicd/syncing-content): Learn how to sync content between environments directly in Prepr for more realistic testing and smoother deployment. - [Syncing a schema](https://docs.prepr.io/development/working-with-cicd/syncing-a-schema): Learn how to sync a schema between environments directly in Prepr using your favorite repository service. Source: https://docs.prepr.io/development/working-with-cicd --- # Managing content items *Easily manage your content items by using some basic features in Prepr.* ## Introduction This article shows you how to perform the following core tasks to manage your content: - [Create a content item](#create-a-content-item) - [Publish a content item](#publish-a-content-item) - [Manage versions](#manage-versions) - [Delete a content item](#delete-a-content-item) - [Find your content items](#find-your-content-items) > **Using an AI agent?:** > > Use the Prepr MCP server to create content items or update multiple items from your AI assistant. Explore the [Prepr MCP server use cases](https://docs.prepr.io/prepr-mcp-server/use-cases). ## Create a content item To add a new content item: 1. Go to the **Content** tab. Here you'll find a list of all content items in your Prepr environment. 2. Click the **Add item** button to open a list of models to choose from. The available models depend on the permissions in your user role and the [model settings](https://docs.prepr.io/content-modeling/managing-models#settings). ![Add item button](https://assets-site.prepr.io/34mga7dinhtk//add-item-no-favorite-models.png) 3. You can mark your favorite models to show them at the top of the model selection list. By marking the models you usually work with, you no longer need to search through a list of unrelated models. ![Add to favorites example](https://assets-site.prepr.io/2e5xvn2xz51f//add-to-favorites-example.png) 4. Select the model for the type of content you want to create, for example, a web page. Now the content item is created and you can start composing your content. ![content item created](https://assets-site.prepr.io/2x94dah1m5tk//new-content-item-example.png) The shown fields depend on the [model settings](https://docs.prepr.io/content-modeling/managing-models) in the *Schema*. The fields marked with an `*` are required. > **Using an AI agent?:** > > Use the Prepr MCP server to create content from your AI assistant. Check out a [use case to create content from an external source](https://docs.prepr.io/prepr-mcp-server/use-cases#create-a-content-item-from-an-external-content-source) for more details. ### Guidance and help text Depending on the setup of each field, you might see some help text for additional information about the content you need to create. ![help text example](https://assets-site.prepr.io/4nvu70tpiqwa//help-text-examples.png) You might also see some additional highlighted guidance. Check out the [*Help text* field](https://docs.prepr.io/content-modeling/field-types#help-text-field) for setup details. ![help text field examples](https://assets-site.prepr.io/6uzoypqnuv3m//help-text-field-info-box.png) You can also create language variants of your content item in a multi-lingual setup. Check out the [Localization doc](https://docs.prepr.io/content-management/localizing-content#create-a-language-variant) for more details. > **Note:** > > You can use the shortcut `⌘ + S (Mac) / Ctrl + S (Windows)` to save the content item. ### Use AI to generate or optimize text If enabled for your content text fields, you can **Ask AI** to generate or optimize content using different options. - **Enter your own prompt** You can enter your own prompt to generate new content or to adjust existing text like in the example below. ![Dynamic content example](https://downloads-site.prepr.io/67tf1ghlopri-dynamic-content-example.gif) - **Optimize using quick actions** You can simply choose from the list of available actions to request AI to optimize your text quickly. ![AI text optimizing example gif](https://downloads-site.prepr.io/j8f2hmxd6cs-ai-optimize.gif) - **Generate new text using predefined prompts** You can choose to quickly generate text for a field based on a predefined prompt. For example, to generate an SEO title based on the main title of an article. ![AI text generation example gif](https://downloads-site.prepr.io/7cktrlc92psl-auto-generate-text-with-ai.gif) For setup details, check out the [AI text assistant guide](https://docs.prepr.io/ai-text-assistant#generating-text). ## Duplicate a content item When you need to duplicate a content item, you can do so in a couple ways: **From the Content items list** 1. Go to the **Content** tab. 2. Hover over the content item you want to duplicate to make the actions visible, and click the icon. ![duplicate content item list](https://assets-site.prepr.io/bjslkvyc45//duplicate-content-item-from-content-item-list.png) - **From the Content item page** 1. Go to the **Content** tab. 2. Click to open a content item you want to duplicate. 3. Click the button to open the drop-down list and choose the **Duplicate item** option. ![duplicate content item](https://assets-site.prepr.io/7kb2qupzoci3//duplicate-content-item.png) ## Publish a content item When you're happy with the content item that you created and want it to be used and displayed in the web app, all you need to do is publish the content item. ![publish content item](https://assets-site.prepr.io/37m54jshl99k//content-item-detail-publish-actions.png) You can publish a content item directly by clicking the **Publish** button or selecting one of the other publish options in the drop-down list, or by using shortcut keys `⌘ + P (Mac) / Ctrl + P (Windows)` to publish the content item. If your content item has any unpublished child content items, either content references or in a stack field, you need to choose to either publish all content items including the unpublished child items or only the parent content item. ![publish nested items example](https://downloads-site.prepr.io/4ooirmjcsywc-publish-nested-items.gif) When you publish the content item directly, the *Workflow stage* of the content item is automatically changed to *Done*. Check out the [Collaboration and workflows doc](https://docs.prepr.io/content-management/collaboration) for more details on workflow stages. If you want to make changes to a published content item, edit the content item and click the icon to save the changes without publishing them. This means there'll be two versions; your saved version and the published version. You can then publish the saved version of the content item after making your changes. > **Using an AI agent?:** > > Use the Prepr MCP server to publish content from your AI assistant. Check out a [Prepr MCP server guide](https://docs.prepr.io/prepr-mcp-server/getting-started) for more details. ## Schedule a content item When you want content item changes to be published in the future, you can schedule a content item. To schedule a content item, simply click to open the **Publish** actions list and click the **Schedule** option. ![schedule a content item](https://assets-site.prepr.io/5a1t6pmufl39//content-item-detail-schedule.png) Then select the date and time for when you want the content item to be published by the web app. The content item will be marked as *Scheduled* until the date and time that you selected. You can update the scheduled content item any time before the scheduled date and time without needing to reschedule the content item. > **Note:** > > If you schedule a content item, make sure the *workflow stage* is set to *Done* if you want it to be published automatically on the *Publish on* date. Check out the [Calendar view](#calendar) to get a clear overview of scheduled items. To reschedule a content item in the calendar view, you can simply drag and drop the item to the desired date or click the icon and choose the **Reschedule** option. ![Reschedule in calendar view](https://downloads-site.prepr.io/2uzygwj82bep-reschedule-items.gif) Alternatively, you can click the **Reschedule** option in the content item when it's already published or scheduled. When you reschedule a published content item, the *First Published at* date and time is set to the new date and time you choose. ![rescheduling](https://downloads-site.prepr.io/1t3kx6wxot2y-rescheduling.gif) This means you can order the content items in the front end when they're ordered by the *First Published at* date and time. When you choose to schedule a content item, you can also enter the *Unpublish on* date and time for temporary content. When this date and time is reached the content item will be *Archived* and no longer available to the web app. You can see all scheduled content items in the *List view* by filtering on the **Publication status** or the *Kanban view* by viewing the **Done** column for *Scheduled* items. You can remove a schedule from a scheduled content item by opening the **Publish** drop-down list and choosing the **Remove schedule** option. When you remove the schedule the workflow stage of the content item is set to *In progress*. ![remove schedule](https://assets-site.prepr.io/6s1g885ck2wh//content-item-detail-remove-schedule.png) ## Preview content items You have a couple of options to preview content items to check what your content changes look like in the web app directly from Prepr. ### Visual Editing You can click the icon to open Visual Editing. Visual Editing is a live preview of a content item in a side-by-side view. Simply save the content item to see your content changes instantly in this view. ![Visual editing icon](https://assets-site.prepr.io/4den54jvykwf//content-item-detail-page-highlight-visual-editing-icon.png) When editing content in this live preview mode, you'll notice the following additional options: - You can choose different screen sizes to get an accurate view of the page on different devices. ![Visual editing - screen sizes](https://assets-site.prepr.io/2wynl6zv6k8//content-item-detail-page-highlight-screen-sizes.png) - When multiple URLs are enabled for Visual editing, for example, for test and development environments, you can switch to a different environment. ![Visual editing - choose URL](https://assets-site.prepr.io/5n5jv3seqnhj//content-item-detail-page-switch-environments.png) To set up Visual Editing, check out the [setup doc](https://docs.prepr.io/project-setup/setting-up-previews-and-visual-editing). ### Preview in a separate tab Another option to preview your content item is to click the icon to open the preview in a separate tab. ![preview in separate tab](https://assets-site.prepr.io/6rc70feyxksc//content-item-detail-preview-in-separate-window.png) To set up one or more preview URLs, check out the [setup doc](https://docs.prepr.io/project-setup/setting-up-previews-and-visual-editing). ## Manage versions Content item versioning enables you to do the following tasks: - Reset your last content item edits. - Keep track of all the changes in a content item. - Revert to a previous version when needed. - Look for older versions in your content item. - See every change made in a content item. You can find content item versions in one of the following ways: - Click the date link in the publish info text at the top left to open the activity log. And click the **Show earlier versions** link. ![show earlier versions link](https://assets-site.prepr.io/2siy21uzaf27//content-item-detail-show-earlier-versions.png) - Or click the button to open the drop-down list and choose the **Show version history** option. ![show version history option](https://assets-site.prepr.io/4vu9dux4zraf//content-item-detail-show-version-history.png) You can then see the list of previous versions for this content item. ![versioning](https://assets-site.prepr.io/ibn1c4owr7w//content-item-detail-versions-list.png) Select one of the versions and click **View** to open the specific content item version. The yellow notification bar at the bottom of the page indicates that you are viewing an older content item version. To restore a previous version, click **Restore** to open this version for further editing. Take note of the following when viewing earlier versions: - All fields and drag-and-drop elements are versioned. - There is no version history of the slug and workflow settings. - Content item versioning is available per locale. ## View linked content items You can view all the linked items for a content item by following the steps below. ![view linked content items gif](https://downloads-site.prepr.io/70kmn43zrvy1-view-linked-content-items.gif) 1. In the *Content* page, hover over the content item for which you want to view all linked items and click the icon. 2. Or open a content item and click the button to open the drop-down list and choose the **Show linked items** option. ![Content item - Show linked items option](https://assets-site.prepr.io/3qgd1qyujpbh//content-item-detail-show-linked-items.png) 3. In the updated filter criteria, you can choose either: - **From**: to view all content items that includes this content item as a content reference. - **To**: to view all content items this content item links to. ## Share a content item You can share a content item with one or more users who have limited access to content items by following the steps below. 1. Go to the **Content** tab. 2. Click to open a content item you want to share. 3. Click the button to open the drop-down list and choose the **Share item** option. ![share item option](https://assets-site.prepr.io/98ujghrpwr//share-one-content-item.png) > **Note:** > > If you don't see the **Share item** option, [contact our Sales team](https://prepr.io/contact-sales) for more details. 4. In the pop-up window, choose one or more users you want to share the content item with and click the **Save** button. Once shared, the users you selected will see the content item in their content item list. > **Note:** > > When users don't have access to a linked content item, they can directly request access to view or edit it in the content page. > > ![Request access to linked author content item](https://assets-site.prepr.io/36uc3o43vtuo//request-access-workflow.png) ## Delete a content item When you need to delete a content item, you can do so in a couple ways: **From the Content items list** 1. Go to the **Content** tab. 2. Hover over the content item you want to delete to make the actions visible, and click the icon. ![Hover Delete from content item list](https://assets-site.prepr.io/1cc7omcf3eah//delete-a-content-item.png) 3. In the pop-up window, click **Delete** again to confirm the action. - **From the Content item page** 1. Go to the **Content** tab. 2. Click to open a content item you want to delete. 3. Click the button to open the drop-down list and choose the **Delete** option. ![delete option](https://assets-site.prepr.io/4vu9dux4zraf//content-item-detail-show-version-history.png) 4. In the pop-up window, click **Delete** again to confirm the action. > **Note:** > > When you delete a published content item, this item is no longer available to your web app. > If you see a deleted item on your web app longer than expected, contact your development team or web administrator to refresh the app. ### Delete a language variant If you [manage multi-language content](https://docs.prepr.io/content-management/localizing-content), then you also have the option to delete a specific language variant. Click to delete a content item and then choose a preferred option: ![Delete language variant](https://assets-site.prepr.io/39jmmgu9rt7f//delete-a-language-variant.png) > **Note:** > > It is also possible to delete a language variant using the API. Please refer to the [GraphQL API docs](https://docs.prepr.io/mutation-api/content-items-deleting) for more information. ### Recover a deleted item You can recover a deleted item by clicking the *Deleted* view to list all the deleted content items. Hover over the content item you want to recover and click the **Recover** link. ![recover deleted items example](https://assets-site.prepr.io/5ok2ngnitu1x//deleted-view.png) When the **Recover item?** pop-up window appears, click the **Yes, recover** button to confirm. The restored content item no longer appears in the list of deleted items. Click the **All items** view to see your recovered item. > **Note:** > > When you recover a previously deleted content item, it's unassigned and the workflow stage is set to *To Do*. > Make sure to **Publish** the recovered content item, if needed. ## Bulk actions on content items In some cases you may want to perform certain actions on multiple content items at once. To view and trigger bulk actions, go to the **Content** tab. Hover over the content items and select the checkbox for the content items on which you want to perform the bulk action. ![All bulk actions](https://assets-site.prepr.io/3nhyzuhg1qwq//bulk-actions.png) > **Using an AI agent?:** > > Use the Prepr MCP server to perform bulk actions on content from your AI assistant. Check out a [Prepr MCP server guide](https://docs.prepr.io/prepr-mcp-server/getting-started) for more details. Let's look at each of these actions in more detail. ### Publish now or later In some cases you might want to publish large batches of content items, such as campaigns or updates that span several pieces of content. You can do this by following the steps below. ![publish content items](https://downloads-site.prepr.io/2wdqo6va0hqa-publish-now-or-later.gif) 1. Choose the content items that you want to publish in the content item list. 2. Click the icon to **Publish now or later**. 3. Click the **Publish** button to publish the content items right away. 4. Or if you want to schedule the content items to be published later, choose a future date and time and click the **Schedule** button. 5. Check the number of content items in the confirmation message and click the **Yes, publish** button or the **Yes, schedule** button. And that's it, you've published multiple content items simultaneously. > **Note:** > > If any of the selected content items fail validation, like missing values, then these will not be included in the process. Be sure to check the count of content items in the confirmation message. ### Unpublish now or later In some cases you might want to unpublish large batches of content items, such as outdated articles. You can do this by following the steps below. ![unpublish content items](https://downloads-site.prepr.io/5yz30fifhme6-unpublish-now-or-later.gif) 1. Choose the content items that you want to unpublish in the content item list. 2. Click the icon to **Unpublish now or later**. 3. Click the **Unpublish** button to unpublish the content items right away. 4. Or if you want to schedule the content items to be unpublished later, choose a future date and time and click the **Schedule** button. 5. Check the number of content items in the confirmation message and click the **Yes, unpublish** button or the **Yes, schedule** button. And that's it, you've unpublished multiple content items simultaneously. ### Change workflow stage to In some cases you might want to change the workflow stage of multiple content items, for example, when you've created a batch of content items and need to move them to the *Review* stage. You can do this by following the steps below. 1. Choose the content items in the content item list for which you want to change the workflow stage. 2. Click the icon to **Change workflow stage to**. ![change workflow stage](https://assets-site.prepr.io/4s4zzd5p1bdf//change-workflow-stage-to.png) 3. Choose your desired stage, for example *Review* from the drop-down list. And that's it, you've change the workflow stage for multiple content items simultaneously. ### Assign to In some cases you might want to assign multiple content items to the same user, for example, when you've created a batch of content items and need someone to review them. You can do this by following the steps below. 1. Choose the content items in the content item list you want to assign. 2. Click the icon to **Assign to**. ![change assign](https://assets-site.prepr.io/5b2lhkcg5335//assign-to.png) 3. Choose the user from the drop-down list. And that's it, you've assigned multiple content items to a single user simultaneously. ### Export to In some cases it's necessary to export content from one environment to another, for example, when you want to do system testing in your development environment with realistic content from the production environment. ![export to](https://assets-site.prepr.io/1ro1z7wo4b5d//export-to.png) > **Note:** > > You won't see the action in the list of bulk actions if you don't have access. > This action is usually part of an overall CI/CD process and requires special permissions. > Check out the [Syncing content doc](https://docs.prepr.io/development/working-with-cicd/syncing-content) for more details. ### Share You can share multiple content items with specific users by following the steps below. 1. Choose the content items you want to share with a user, by hovering over each content item and clicking each checkbox. 2. To select all content items, click the icon at the top of the list. ![choose share](https://assets-site.prepr.io/bz1ngz91dbj//share-content-items.png) 3. Click the icon at the top of the list to share all the selected content items. > **Note:** > > If you don't have access to the icon, [contact our Sales team](https://prepr.io/contact-sales) for more details. 4. Choose the user you want to share the content items with. Once shared, the user you selected will see the content items in their content item list. > **Note:** > > When users don't have access to a linked content item, they can directly request access to view or edit it in the content page. > > ![Request access to linked author content item](https://assets-site.prepr.io/36uc3o43vtuo//request-access-workflow.png) ### Delete To delete multiple content items at once follow the steps below. 1. Choose the content items you want to delete, by hovering over each content item and clicking each checkbox. 2. To select all content items, click the icon at the top of the list. 3. Click the icon at the top of the list to delete all the selected content items. 4. In the pop-up window, click **Delete** again to confirm the action. ![select bulk delete](https://assets-site.prepr.io/18a5gwtlo9y4//bulk-delete.png) ## Find your content items There are several features listed below to help you find your content items easily. ### Layout options You can use one of the layout options at the top of the *Content* page to find content items easily depending on your needs. #### List When you go to the **Content** tab, the *List* layout opens by default to show a complete list of all of your content items. You can [filter the content items](#filter-options) to narrow down the list. ![List layout](https://assets-site.prepr.io/3avoslaa72hs//list-layout-option.png) #### Calendar Click the *Calendar* icon to get a clear visual overview of all your scheduled items to help you plan upcoming articles or campaigns to avoid overlap. ![Calendar view](https://assets-site.prepr.io/6ub7e9p244xh//calendar-layout-option.png) #### Kanban Click the *Kanban* icon when you want to see a clear overview of content items by their status. ![Kanban view highlight icon](https://assets-site.prepr.io/6z436rjjzmmt//kanban-layout-option.png) #### Content tree Click the *Content tree* icon when you want to see an overview of the relationship between content items in a tree-like hierarchy. The content tree is based on the slug value of content items. > **Note:** > > The content tree is only meaningful when the slugs are defined consistently for all content items. > For example: The blog overview page is a parent content item and has the slug value `blog`, while all individual blog articles are child items and have slugs like `blog/article-name`. ![Content tree example](https://assets-site.prepr.io/5ur92tb7k8ym//content-tree-example.png) If the content tree layout is not visible, request an admin user to [define the parent slug format](https://docs.prepr.io/project-setup/setting-up-environments#content-tree-parent-slug-format). ### Filter options Prepr makes it even easier to filter precisely the content you're looking for with **Advanced Content Filters**. You can choose multiple filters to narrow down your search for specific content items. See the complete list of filters below. ![content item filters](https://assets-site.prepr.io/1yortdj7qra9//filter-options.png) #### Locale You can filter content items by a specific locale, for example, `en-US` to select the list of American English versions of content. #### Model You can filter your content by one or more models. For example, to only list the blog posts or to view a list of pages and blog posts. - *Supporting model* - When you choose a model, additional filters are available for all supporting models. You can choose from a list of models that are defined as content references, for example, the **Category** of the *Car* model. - *Stack* - When you choose a model, additional filters are available for any stack fields defined in the model. The filter name matches the name of the [*Stack* field](https://docs.prepr.io/content-modeling/field-types#stack-field). - *Enumeration* - When you choose a model, additional filters are available for any enumeration used in the model. The filter name matches the enumeration name, for example the **Size** for the *Product* model. > **Note:** > > The *Supporting model*, *Stack* and *Enumeration* sub-filters are only available when you filter a single model. #### Publication status You can filter your content by one or more publication statuses. For example, to list published and scheduled content items. The possible filter values are *Published*, *Scheduled*, *Unpublished changes* and *Not Published*. #### Workflow stage You can filter your content by one or more workflow stages. For example, to list the content items that are in *To do* or *In progress*. The possible filter values are *Done*, *Review*, *In progress*, *To do*, and *Archived*. #### Assignee You can filter your content by one or more assignees. #### Publication date You can filter content items by selecting a date range, including future dates, for example, to filter items scheduled to be published in the future. #### Tags You can filter content items by one or more tags. ### Views You can see the list of views in the left sidebar of the **Content** page. ![view introduction](https://assets-site.prepr.io/4qkbanvaha96//views-introduction.png) The following views are available out of the box and cannot be updated: - *All items* - The default view of all content items without any filter criteria. - *Scheduled* - The list of content items that are scheduled for a future date. - *Experiments* - A view of all content items with adaptive content and A/B tests, in other words a complete list of all your experiments. - *Needs attention* - The list of content items with broken links or could not be published. In this view, you can go to the relevant content - *Deleted* - The list of deleted content items. In this view, you can recover content items that were deleted by mistake, for example. Apart from these views, you can create your own *Shared view* or *Private view*. A *Private view* will only be visible to you, while a *Shared view* will be visible to all users of this environment. When you're viewing all content items and choose some filter criteria, these are retained for a session. So the next time you visit the overview of the content items, the last selected filters will be active. If you often use the same filters in your Prepr environment, you can save these as a view. Make the filter selection and click the **Save as new view** link. Give the view a name and save it. You can also group similar views by moving them into folders. ![content list views example](https://assets-site.prepr.io/b0owkhy5rws//content-list-view-shared-view-in-folder-selected.png) ### Search content items When you type any keyword in the search bar, Prepr performs a fuzzy search on the *Title* field of content items, by default. When you click the search bar, a drop-down list appears with the following additional search options: - *Search on full-text* - Prepr performs a fuzzy search on all text fields and text elements. - *Search on slug* - Enter a partial or exact keyword for this fuzzy search on the *Slug field*. - *Search on ID* - Enter the exact ID of the content item you are looking for. ![content item search](https://assets-site.prepr.io/4c6irdkql27w//search-options.png) Now that you understand each of the core tasks in more detail, review the handy shortcuts below to manage content. ## Manage content items with shortcut keys You can use a number of shortcut keys to manage content items more efficiently. Go to the **Content** tab to view the content item list. From this view, you can use the following shortcuts: - Add a new content item with `⌘ + I (Mac) / Ctrl + I (Windows)`. If the content item list is filtered by a specific model, this shortcut creates a content item for that model directly. - Select all content items in the list with `⌘ + A (Mac) / Ctrl + A (Windows)`. - Simply press the `esc` key to deselect any selected content items. - Delete selected content items from the list by pressing the `⌫ delete (Mac) / backspace (Windows)` key. Choose and click a content item in the list to make changes to that content item. From a specific content item, you can use the following shortcuts: - Publish the content item with `⌘ + P (Mac) / Ctrl + P (Windows)`. - Save the content item with `⌘ + S (Mac) / Ctrl + S (Windows)`. - Close the content item with `⌘ + L (Mac) / Ctrl + L (Windows)`. From any other page in Prepr, you can press `/` to open the launchbar. The launchbar allows you to do quick searches on content items or any other page you have access to in your Prepr environment. Source: https://docs.prepr.io/content-management/managing-content/managing-content-items --- # Optimizing content for SEO with *Content check* *This article explains how to optimize your content items for SEO (search engine optimization).* ## Search engine optimization SEO (search engine optimization) is the practice of improving your website's content to increase visibility and ranking in a search engine's results with the goal of driving more relevant traffic. You don't need to be an SEO expert to create high-ranking content in search engines when using Prepr. When you want to create optimal SEO values, simply run the *Content check* feature while editing a content item. > **Note:** > > To get the most relevant SEO suggestions from the *Content check* feature, it's important to have the [environment description](https://docs.prepr.io/project-setup/setting-up-environments#define-environment-context-for-ai-requests) defined to provide more context for the AI requests. ## *Content check* The [*Content check*](https://docs.prepr.io/content-management/reviewing-content#content-check) feature automatically checks a content item for nonoptimal SEO values and makes suggestions you can choose to apply, adjust or ignore. ![Example content check results with SEO values](https://assets-site.prepr.io/3qvump73qbv0//content-check-seo-suggestion-example.png) For setup details, check out the [model settings](https://docs.prepr.io/ai-text-assistant#checking-seo-values). ## SEO values Prepr checks the following SEO values when you run the *Content check* feature: - SEO title - Meta description - Keywords The SEO title and meta description values come from fields in a model or component that are marked as the **SEO title** and **Meta description**. Check out the [field setup guide](https://docs.prepr.io/content-modeling/field-types#item-title-and-seo-fields) for more details. ![Example SEO component with SEO title and meta description](https://assets-site.prepr.io/27cf5q7gr2cy//example-seo-component.png) In addition to checking the length of the SEO title and meta description, the *Content check* makes AI suggestions for alternative values that meet the guidelines: - SEO title - Recommended length between 50 and 60 characters - Meta description - Recommended length between 120 and 160 characters - Keywords - If keywords exist, they should exist in the SEO title or meta description, or they shouldn't be duplicated too many times in the SEO title or meta description. ## Adding keywords When you run the *Content check* for a content item, and there are no keywords, you can add keywords to improve the SEO for this content item by clicking the **Add keywords** link. ![example content check - add keywords dialog box](https://assets-site.prepr.io/49i3fjhjys7v//add-keywords-example.png) For best practices on handling SEO for development, check out the [SEO best practices guide](https://docs.prepr.io/development/best-practices/seo). Source: https://docs.prepr.io/content-management/managing-content/optimizing-content-for-seo --- # Creating rich content *Easily create rich content using basic features in Prepr.* ## Introduction This article shows you how to enrich your content in different ways: - Using the *Dynamic Content Editor* to add and format text, media, and structured elements. - Adding content references to link related content items. - Adding links to reference external resources. - Adding remote content from external system. - Working with a **Stack** field to build and manage the sections of a component-based page. ## Using the Dynamic content editor ![Dynamic content editor](https://assets-site.prepr.io/ia6xh9gccby//creating-rich-content-dynamic-content-editor.png) In the *Dynamic Content Editor*, you can enrich your content using the following elements: - **Paragraph** The paragraph element contains a number of formatting options. You can use **bold text**, *italic*, and underline. Bullets (ordered and unordered), links, and tables are also available. - **Headings** Depending on the settings, you can choose up to six levels of headings to match your content item. You can easily change a heading element to another heading or other text element by making a selection from the drop-down options. - **List** You can add an ordered or unordered list. Like the paragraph, you can apply a number of formatting options. - **Table** You can add a table with up to 10 rows and 10 columns. - **Code** You can paste a code snippet in your content, for example, HTML. - **Media** Depending on the settings on the [dynamic content field](https://docs.prepr.io/content-modeling/field-types#dynamic-content-field), select the icon to add one or more images, videos or files. You could also edit captions, set the alignment or define image presets. [Learn more about image presets and alignment](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets). - **Social post embeds** Social elements are embeds from the largest social media platforms, namely, Twitter, Facebook, Instagram, Spotify, Youtube, SoundCloud, vimeo, TikTok, Apple Podcast, Bluesky, and Threads. To embed a social post, choose the corresponding social media icon and insert the URL of the social post. - **Location** Click the icon to add a location based on a Google Map address or coordinates. - **Integrations** If you need to reference content in an external CMS, a legacy system or an ecommerce platform, click the corresponding integration icon and choose the integrated content. Prepr will keep the data in sync automatically. Check out more details in the [integrations](https://docs.prepr.io/integrations) docs. - **Component** If a component is defined and included in the settings of the dynamic content field, you can embed a component by clicking on the corresponding icon. By using a component, you can add empty elements to your dynamic content. This way you can trigger the front end to insert static front-end components, such as banners, marketing widgets, or forms. Check out more details in the [components](https://docs.prepr.io/content-modeling/managing-components) doc. > **Note:** > > **Nested dynamic content field** \ > > > You cannot add content to a dynamic content field nested in a dynamic content field. > When you see the message below while editing dynamic content, it could mean your content includes a nested dynamic content field or there're too many nested elements. > > ![content item warning message for deeply nested components](https://assets-site.prepr.io/5jzu32fnlvtn//content-item-warning-message.png) > > In this case, your team may need to review [the content structure](https://docs.prepr.io/content-modeling/field-types#dynamic-content-field). There are several ways to edit your content in the *Dynamic Content Editor*: - **Add an element.** To add a new element, do one of the following: - Enter **/** to see a pop-up with all the elements. - Click an element icon in the toolbar below. - Press ENTER to add a new paragraph and SHIFT ENTER to add a break line. - From the element icon on the left, click the **Add element below** button. - **Multi-select *Text* elements.** You can select multiple text elements to copy or delete them. - **Change the element type.** To change an element to another element type, click the element icon on the left and select the new element type from the drop down. Note that you can only change a filled element type to an element of the same type. For example, a paragraph can be changed to a heading, but not to a media file. - **Sort elements.** To change the position of an element, click the element icon on the left and click the or button. - **Remove elements.** To remove a text element from the dynamic content field, do one of the following: - Empty the element and enter BACKSPACE. - Click the element icon on the left and click the button. ## Adding content references The content reference is a super-fast way to link related items to your content item. The content reference field is often used for linked content items, such as authors, categories, or as manually picked related items. There are three options to display the content reference field, depending on your needs: as a modal window, as an autosuggest, or as checkboxes/radio buttons. You can make your choice in the [Reference field settings](https://docs.prepr.io/content-modeling/field-types#content-reference-field). - **Modal window**. This option combines searching and adding new items. You can also add a filter to quickly find the related item you need. If the item you need is not present, you can create it there and then. ![Content item modal example with filter options](https://assets-site.prepr.io/66g35692of4g//new-content-item-modal-with-example-filter.png) - **Auto-suggest**. You can use autosuggest if you need a specific content item quickly, without having to search extensively. Start typing in your input field and the results will appear immediately. ![auto suggest](https://assets-site.prepr.io/1nrsjp8sn6ig//creating-rich-content-content-reference-dropdown-list.png) - **Checkboxes**. This option is best used if you only have a limited set of content items to choose from. An example is a topic or author you want to refer to. When you have indicated a maximum of 1 in your model, the options will be shown as a radio button. ![checkboxes](https://assets-site.prepr.io/3vthzhk0g89p//creating-rich-content-content-reference-checkboxes.png) > **Note:** > > The checkbox view mode lists only the first 50 items. To see the full content item list, choose an alternative view mode. > **Note:** > > When you hover over the title of the content item, the icon appears. Click it to open this content item for editing. ## Adding internal and external links ### Dynamic internal links The Internal links are references to items or assets in Prepr. You can refer to other content items or assets in a text editor or the *Dynamic Content Editor*. To create an internal reference, click the **Insert item link** icon in the toolbar of the text editor and choose whether you want to link to a content item or an asset. Select the item or asset in the media browser and insert the link. ![dynamic internal links](https://assets-site.prepr.io/231oavgvnsoq//creating-rich-content-internal-link.png) The created link is dynamic, this means that as soon as the title, slug, or URL of the linked content item or asset changes, the link will update dynamically. This way you never have to deal with dead links in your text again. Check out the [dynamic content field API reference](https://docs.prepr.io/graphql-api/schema-field-types-dynamic-content-field#links-text) on how to query the internal link. ### External linking options You can link to an external URL using the **Link option**. If *Links* are enabled on a text field or the *Dynamic content Editor*, you can add an external link to your text. To add an external link, click the icon to make an in-line reference. Enter the URL, and the link text, and decide whether this link needs to be opened in a new tab or not. For SEO purposes, you can also select the Don't follow option. ![insert link](https://assets-site.prepr.io/739x5wty2hev//content-item-detail-dont-follow-tag.png) To edit the added link, simply click anywhere on the link text and click **Edit link** to make changes in the URL or in the link text. ## Adding remote content When you need to add content that is sourced from an external system, and the [remote source is set up in Prepr](https://docs.prepr.io/content-modeling/creating-a-custom-remote-source), you can simply you’ve connected to the external system and you can add the remote content to a content item using the following steps: 1. Navigate to the **Content** tab and click the desired content item from the list. 2. In the remote content section, click the button to add new items (in our example – **Add product**), search through the catalog or use a filter to find and add the desired items to the content item. 3. Save and publish this content item to complete the setup. ![Add remote content to a content item](https://assets-site.prepr.io//6ahgjm6yqg73-choose-remote-items-with-filters.png) That’s it. Now your web page includes 3rd party content. Prepr will synchronize your remote content automatically to keep it up to date. ## Working with a Stack field The *Stack* field is where you build and manage the sections of a component-based page. You can see it in the example below as the *Content* section with a few components. ![stack field example](https://assets-site.prepr.io/47k3i1zghi3j//example-stack-field.png) ### Add a content item or component Simply click the **+ Content item or component** button to add a new element to the stack. This opens up a popup modal to help you find the content item or component you want to add. ![popup search for content item or component](https://assets-site.prepr.io/11jn7ufowln2//select-model-or-component-popup.png) ### Reorder elements in a stack To change the order of elements in the stack, click and hold the element, then drag it to the desired position and release. The order you set here is the order in which the sections appear on the published page. ### Add an A/B test ![A/B test icon](https://assets-site.prepr.io/14dy5yjj23x1//ab-test-option-on-stack-field.png) Check out the [A/B testing guide](https://docs.prepr.io/ab-testing/running-ab-tests) for more details on A/B testing. ### Personalize an element ![Personalize icon](https://assets-site.prepr.io/4wbk6tlkf8fj//personalize-option-on-stack-field.png) Check out the [Personalization guide](https://docs.prepr.io/personalization/managing-adaptive-content) for more details on personalizing content. ### Duplicate an element When you need to make a quick copy of an element just below it, you can use the *Duplicate* option. Simply, hover over the component or content item in the stack field and click the icon to duplicate the element. ![Duplicate action in stack](https://assets-site.prepr.io/75al5m1rxtn9//duplicate-an-element.png) ### Copy an element You can also copy an element to paste it to another stack field. Hover over the component or content item in the stack field and click the icon to copy the element. Go to the stack field where you want to place the component or content item and click the **Paste** button. ![Copy and past actions in stack](https://assets-site.prepr.io/4jrit25jpvcz//copy-and-paste-element.png) ### Delete an element To delete an element in the stack field, click the icon. ![Delete an element](https://assets-site.prepr.io/sl58xyeb67u//delete-an-element.png) Then, click the **Yes, delete** button to confirm the deletion. Source: https://docs.prepr.io/content-management/managing-content/creating-rich-content --- # Managing content *Learn about managing content items, improving SEO and readability and creating rich content in Prepr CMS.* - [Managing content items](https://docs.prepr.io/content-management/managing-content/managing-content-items): Create, publish and preview content items. - [Optimizing content for SEO](https://docs.prepr.io/content-management/managing-content/optimizing-content-for-seo): Optimize your content items for search engines using the Content check feature. - [Creating rich content](https://docs.prepr.io/content-management/managing-content/creating-rich-content): Enrich your content using maps, components, links and data from external sources. Source: https://docs.prepr.io/content-management/managing-content --- # Introduction to Assets *From this article, you’ll learn what assets are and which asset types are available in Prepr.* ## What are assets? *Assets* are media files such as images, video, audio, and other digital files you can use on your web pages to enrich content. With the growing demands of an internet business, brands have to do more to stand out and meet the rising expectations of potential customers. Rich content has a crucial role in attracting and keeping your audience. Prepr allows you to easily create content pages filled with rich media assets. Let's look at them in detail. ## Assets in Prepr An *Asset* in Prepr is a media file, such as an image, audio, video, live stream, or another file type, that lives in one organized storage — [*the Media Library*](https://docs.prepr.io/content-management/managing-assets/managing-assets). You only need to upload digital assets to Prepr once, and you can reuse them on multiple web pages. Prepr automatically deploys and serves all media assets over a high-performing *Content delivery network (CDN)*. A CDN stores a cached version of content and puts it in many places simultaneously. This improves page load speed and accelerates content delivery. Prepr supports the most popular image formats and provides automatic image optimization with *WebP encoding technology*. The WebP format makes your web pages load faster, frees up storage space, and guarantees high image quality. The following table lists the asset types in Prepr and their supported media formats. | Asset type|Description|Supported formats| | -------------------------------------------- | ----------------------------------------------|---------------------| | [Image](https://docs.prepr.io/content-management/managing-assets/introduction-to-assets#image) | Image files such as photos. | JPG, JPEG, PNG, BMP, SVG, GIF and more. \ \ **Note:** WebP graphics are supported through the *File* asset in Prepr. | | [Video](https://docs.prepr.io/content-management/managing-assets/introduction-to-assets#video-and-audio) | Video files and live video streams. | MOV, M4V, MPG, MP4, and more. | | [Audio](https://docs.prepr.io/content-management/managing-assets/introduction-to-assets#video-and-audio) | Audio files. | MP3, M4A, WAV, and more. | | [File](https://docs.prepr.io/content-management/managing-assets/introduction-to-assets#file) | Archives, documents, and vector images. | The file upload recognizes the ZIP, PDF, XLSX, DOCX, and WebP extensions. Files with any other extension are uploaded as documents. | ### Image There’s a wide range of image options in the Prepr Editor interface and through the API. For example, image presets for different device types, cropping, captions, and more. Read more about [adding images to your web application](https://docs.prepr.io/development/best-practices/assets/images). > **Note:** > > WebP graphic files cannot be processed through the image pipeline. To work with WebP graphics, use the *File* asset in Prepr. ### Video and Audio You can enrich your web pages with audio and video content or broadcast live video streams thanks to the *Prepr and Mux Video integration*. This integration gives you the benefits of seamless multi-channel content delivery and cutting-edge video processing technologies. *Mux Video* accepts most modern video formats and codecs. And you get automatic video transcoding, audio normalization, and high-quality live streaming with the *HLS (HTTP Live Streaming)* protocol out of the box. Prepr uploads your media files to Mux automatically, so you do not need to have a Mux account yourself. Follow our step-by-step guides to [post audio and video content](https://docs.prepr.io/development/best-practices/assets/video-audio) or [stream your live video](https://docs.prepr.io/development/best-practices/assets/live-video-stream) in a web application. ### File With Prepr, you can add different file types to your web application, such as PDF, ZIP, and more. You can also use the *File* asset to work with WebP graphics in Prepr. Find more details in the [Files guideline](https://docs.prepr.io/development/best-practices/assets/files). ## Where to start? To start with, learn how to create and where to store assets in Prepr. Please check out the [Manage assets](https://docs.prepr.io/content-management/managing-assets/managing-assets) guide. Source: https://docs.prepr.io/content-management/managing-assets/introduction-to-assets --- # Managing assets on the *Media* page *This article explains where assets live in Prepr and which actions you can perform on them.* ## Introduction You can find all your digital assets in one place in Prepr using the **Media** page. This makes it easy to access, view, and manage your assets. > **Note:** > > **Developer note**\ > > With the [Prepr assets mutation API](https://docs.prepr.io/mutation-api/managing-assets), you can upload, update, and delete assets in batches, or apply image transformation options. You can directly upload, edit, download, replace, delete, organize, and find assets from the *Media* page. For Mux videos, you can also replace thumbnails and add subtitles. Let's dive into each of these actions in more detail. > **Using an AI agent?:** > > Use the Prepr MCP server to upload, edit, organize, and delete assets from your AI assistant. Check out the [Prepr MCP server guide](https://docs.prepr.io/prepr-mcp-server/getting-started) for more details. ## Uploading assets To upload an asset, follow the steps below. ![Drag and drop assets into media library](https://downloads-site.prepr.io/1xpvxdt8in2z-upload-assets.gif) 1. In the **Media** page, simply drag and drop the asset/s directly from your local drive. 2. Or at the top of the **Media** page, click the **Upload asset** button to open your file explorer and choose the asset/s you want to upload and click the **open** button. ![Upload asset button example](https://assets-site.prepr.io/lpp1vxfizxy//upload-asset-button-example.png) You'll see the progress of the assets in each thumbnail while they're uploading. 3. If any required fields are not filled during the upload, click the **Continue** button to enter the required values. Click the **Add x asset(s)** button to save the changes. ![Fill required Asset text field example](https://assets-site.prepr.io/3b2m0buvqrk//fill-required-fields.png) Once done, you can then click the thumbnail of the assets you want to view. > **Note:** > > You can also upload an asset directly in your content item. Check out the [using assets in content items guide](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets#adding-assets-to-a-content-item) for more details. ## Editing assets To edit a specific asset, go to the **Media** tab and click the thumbnail of the asset to open it. You can update some of the asset fields below directly in the *Asset* page. ![Asset details](https://assets-site.prepr.io/355nusehfhb0//asset-detail-page.png) - **Internal name** - This field is required and is used to enter the *Title* of the asset. - **Description** - This optional field is used by editors to describe the asset. - **Author** - This optional field can be used to enter the name of the photographer or the visual designer of the asset. - *Additional fields* - Any additional fields can be added to the **Asset** model, such as **Alt text**. For more details, check out the [Asset model doc](https://docs.prepr.io/content-modeling/defining-the-asset-model). > **Note:** > > If enabled, you can AI generate text for these fields. Check out the [AI image text guide](https://docs.prepr.io/ai-image-text) for more details. - **Tags** – Allows you to [add one or more tags to an asset](#tags). - **Folders** – Allows you to [group assets into folders](#folders). - **Used in** - The list of all content items that use this asset. To edit the asset details, simply update the applicable fields and click the **Save and close** button. > **Note:** > > The *Description* field is recommended for internal use. To describe your assets in a web application, use the [*Caption*](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets#adding-captions) feature.\ > > > **Developer note**\ > > If you want to add information from the *Description* field to your web application, refer to the [GraphQL API reference](https://docs.prepr.io/graphql-api/schema-field-types#assets). Hover over the **File info** text to see the asset metadata such as the file type. ![Asset file info example](https://assets-site.prepr.io/22q4iaqbavsu//asset-file-info-example.png) Click the icon to copy one of the following data values generated when this asset was uploaded. ![Generated data examples](https://assets-site.prepr.io/34m8u83wygb3//asset-copy-generated-data-example.png) - *Optimized URL* – The URL you can use to include the asset in your front end. - *Playback ID* – The public Mux Playback ID for videos. - *Download URL* – The URL you can use to download an original asset file. - *Asset ID* – The unique identifier of an asset you can refer to in API requests. - *Reference ID* - The unique internal asset identifier, for example an ID from an external system. ## Multiple locale assets When localization is enabled in the Asset model, additional fields will be available for each locale allowed in your environment. ![Example asset - multiple locale field](https://assets-site.prepr.io/11cwrd69mm78//asset-multiple-locales.png) For more details on the setup, check out the [Asset model doc](https://docs.prepr.io/content-modeling/defining-the-asset-model#enable-localization-in-assets). ## Downloading an asset In the *Asset* page, you can download this asset to your local storage, by clicking the button. ![Download an asset](https://assets-site.prepr.io/1wbohdk34sfj//asset-download.png) The original file will be saved to your default download folder. ## Replacing an asset In the *Asset* page, you can replace an existing file with a new one from your local storage by clicking the button. ![Replace an asset](https://assets-site.prepr.io/3am24eayyyi7//asset-replace.png) > **Note:** > > You can only replace an asset with the same file type — for example, images with images, videos with other videos, and so on. ## Deleting assets In the *Asset* page, you can delete an individual asset if you no longer need it, by clicking the icon and choosing the **Delete** option. ![Delete an asset](https://assets-site.prepr.io/39jjfmbbthgl//asset-delete.png) In the confirmation dialog window, click the **Yes, delete** button to confirm the delete. > **Note:** > > To prevent broken web pages, you cannot delete assets that are used in content items. You can also delete multiple assets by following the steps below. 1. Go to the **Media** tab and select assets by hovering over each thumbnail and clicking the icon. To select all assets, click the icon in the top action menu. 2. In the top action menu, click the icon to delete the selected assets. ![delete multiple assets](https://assets-site.prepr.io/67nfit50y9s4//bulk-delete.png) 3. In the confirmation dialog window, click the **Yes, delete** button. > **Note:** > > You need special permissions to delete multiple assets at a time. > To get this access, request your administrator to link your profile to a user role with the **Content → Bulk update** permission enabled. ## Organizing assets In Prepr, you can organize your assets in a way that works for you. For example, choose to group assets into folders, add tags, or do both for better categorization and precise search. ### Folders A *Folder* contains media files grouped by specific attributes and includes all types of assets. It can be a group of profile pictures, banners, stock photos, etc. Let's look at how to organize assets using folders in more detail. #### Create a folder of assets To create a folder, follow the steps below. 1. Go to the **Media** page. 2. Under the *Folders* section on the left, click the **+ Add folder** link. ![Add a collection and assets](https://assets-site.prepr.io/6d398a7w1m1z//adding-image-folders.png) 3. Enter the title for a folder and click the **Add** button to confirm. 4. You can then add assets to the folder directly by choosing the assets and clicking the **Add x assets** button, or by clicking the **Upload assets** button to upload new assets. ![add assets to folder](https://assets-site.prepr.io/4tlybo9ok8xa//add-assets-to-a-newly-created-folder.png) 5. For existing folders, you can simply select assets to drag and drop them into the folder directly. ![Add assets to collection](https://downloads-site.prepr.io/4lpusf1yy3p7-add-assets-to-folder.gif) 6. Or you can add assets directly in the folder, by clicking it to open it. From here, click the **Add assets** button to open the *Media browser*. a. In this window, you can then select and drag the assets you want to add to the folder or select the assets and click the **Add x assets** button. b. If the images you want to add to the folder are not yet uploaded, click the **Upload assets** button to upload new assets and add them to the folder. To add just a single asset to a folder in the asset detail page. 1. In the **Media** page, click the thumbnail of an asset to open it. 2. At the bottom of the *Asset* detail page, click the **+ Add to folder** link in the *Folders* section. 3. Choose a folder from the drop-down menu. 4. To remove the asset from the folder, simply click the icon. 5. Click the **Save and close** button to apply the changes. #### Update the folder title To update a folder title, under the *Folders* section on the left, hover over the folder you want to edit. Then click the icon and choose the **Edit folder** option. ![edit folder](https://assets-site.prepr.io/171n9hi80omn//edit-folder-title.png) In the dialog window, enter a new title and click the **Save** button. #### Delete a folder To delete a folder, under the *Folders* section on the left, hover over the folder you want to delete. Then, click the icon and choose the **Delete folder** option. In the confirmation window, click the **Yes, delete** button. > **Note:** > > When you delete a folder, the assets are removed from the folder, but not deleted. ### Tags A *Tag* is a meaningful label you can assign to your assets to differentiate between them while filtering. To tag a single asset, follow the steps below. ![Asset tags](https://assets-site.prepr.io/6w06mqmx5q8b//asset-tags.png) 2. In the *Tags* section, choose an existing tag or type a new one. 3. Click the **Save and close** button to apply the changes. That’s it. You have organized your assets in the Media Library. Now it's easier to find the right one. Let's see how to do this in the next paragraph. ## Bulk actions on assets In some cases you may want to perform certain actions on multiple assets at once. To view and trigger bulk actions, go to the **Media** tab. Hover over the assets and select the checkboxes for the assets on which you want to perform the bulk action. ![Example assets bulk actions](https://assets-site.prepr.io/7dyu4dqefff3//asset-bulk-actions.png) > **Note:** > > To view assets more easily for certain bulk actions, you can click the icon to change from the default *Grid view* to the *List view*. > > ![List view](https://assets-site.prepr.io/3ec6d3bkvja6//list-view-of-assets.png) ### Edit To edit multiple assets at a type, select the checkboxes for the assets you need to edit and click the icon. ![Bulk Edit assets example](https://assets-site.prepr.io/4bk8y550mqrq//bulk-edit-assets.png) When you finish editing one asset, click the **Save and next asset** button to open the next asset. Continue editing until the last asset, then click the **Save** button. ### Add to folder You can add multiple assets to a folder by selecting the checkboxes for the assets you need to group and click the icon. ![Add to folder example](https://assets-site.prepr.io/78l7glg5puyr//assets-bulk-action-add-to-folder.png) Select the folder from the dropdown list and click the **Save** button. ### Tag To tag several assets at once, follow the steps below. 1. Go to the **Media** page and in the *All assets* view, select the assets you want to tag by hovering over each thumbnail and clicking the icon. 2. In the top action menu, click the icon. ![Tag multiple assets](https://assets-site.prepr.io/bpbsfftpi10//tag-multiple-assets.png) 3. Choose an existing tag or type a new one. 4. Click the **Add tags** button. ### Remove from folder You can remove multiple assets from a folder, by first clicking the folder name to open it and view the assets in that folder. Select the assets you want to remove by hovering and clicking the icon on each of the thumbnails. Click the icon in the top action bar to remove the assets from this folder. ![Remove multiple assets from a collection](https://assets-site.prepr.io/1165ld4jd3e5//bulk-action-remove-assets-from-folder.png) Click the "Yes, remove" button to confirm. ### Delete To delete multiple assets at once, follow the steps below. 1. Go to the **Media** page and in the *All assets* view, select the assets you want to tag by hovering over each thumbnail and clicking the icon. 2. In the top action menu, click the icon. ![Delete multiple assets](https://assets-site.prepr.io/49iocfq6bdw5//delete-multiple-assets.png) 3. Click the "Yes, delete" button to confirm. ## Finding assets Prepr gives you multiple search and filter options to find the assets you need with ease: - Search the assets by the asset title or keywords in the asset description. - Filter the assets by *Asset type*, *Tags*, *Created on* date, or the *Unused* checkbox. - You can also filter assets by [enumeration field values in the Asset model](https://docs.prepr.io/content-modeling/defining-the-asset-model#add-fields-to-the-asset-model), for example, to find a specific license holder. ![Filter and search assets](https://assets-site.prepr.io/57psueyhuojj//asset-filter-options.png) ## Replacing video thumbnails By default, a thumbnail image is taken from the middle of the video based on the Mux *Playback ID*. Still, you can set a custom video thumbnail in Prepr as follows: 1. Go to the ** Media** page and click the thumbnail of the video asset that you want to edit. 2. On the asset detail page, hover over the thumbnail and click the icon. 3. Choose an image file to replace the thumbnail. ![Replace thumbnail](https://assets-site.prepr.io/6wute0b1iolu//asset-replace-thumbnail.png) > **Note:** > > **Developer note**\ > > You can retrieve a video thumbnail using the `cover` attribute in your API request. Check out the [GraphQL API reference](https://docs.prepr.io/graphql-api/schema-field-types#video) for more details. ## Adding video subtitles You can add subtitles to your Mux videos in Prepr to provide multilingual support to your web app visitors and extend the web app accessibility. > **Note:** > > The *Subtitle* feature is only compatible with the Mux web player. Read more in the [Mux documentation](https://docs.mux.com/guides/add-subtitles-to-your-videos). To add subtitles, follow the steps below. 1. Go to the ** Media** page and open the video file to which you want to add subtitles. 2. On the asset detail page, click the **+ Add Subtitle** link. 3. Choose a locale, and upload either an *SRT* or *WebVTT* file containing the subtitle information. Prepr will automatically generate a subtitle URL and add it to the video in Mux. ![Add subtitle](https://assets-site.prepr.io/nn1xwv36awg//asset-video-subtitles.png) There's no limit on the number of subtitle files you can include in your video asset. Each subtitle file will be stored as an individual file asset in Prepr. [Learn more on how to add videos to your web app](https://docs.prepr.io/development/best-practices/assets/video-audio). Source: https://docs.prepr.io/content-management/managing-assets/managing-assets --- # Using assets in content items *This guide describes the Prepr features you can use to edit and configure assets when adding them to your web application.* ## Introduction Media files can enrich your website content only when they are qualitative and usable. For instance, images should be optimized for a device and presented at the highest resolution possible. Likewise, having a well-described video with the key cues in your audience’s native language increases the chances your visitors will watch the content. In other words, the way you configure your assets significantly affects your content usability. The following options are available for you to edit and configure assets in your content item: - Adding captions - Setting alignment - Setting an image focal point - Cropping images - Setting a download filename Before configuring your assets though, let's first look at how to add assets to a content item. ## Adding assets to a content item If [asset fields are defined in the model](https://docs.prepr.io/content-modeling/field-types#assets-field) of your content item, you'll see an asset block for each asset field. Check out more details on [the supported assets in Prepr](https://docs.prepr.io/content-management/managing-assets/introduction-to-assets#assets-in-prepr). To add assets to a content item, simply drag and drop the asset file from your local storage to this block, or click the **browse** link to open your local file explorer, or click the **select** link to open the media window dialog to choose an existing file in Prepr. ![Add assets to content items](https://downloads-site.prepr.io/6kw4hyba1sdv-add-asset-to-content-item.gif) If any required fields are not filled during the upload, click the **Continue** button to enter the required values. ![Fill required Asset text field example](https://assets-site.prepr.io/3b2m0buvqrk//fill-required-fields.png) Click the **Add 1 asset** button to complete the process. Now that you know how to add assets to a content item, let's dive into different display options for the images. ## Setting alignment The *Alignment* feature lets you position a digital asset within the text elements on a web page. You can choose from the following options: - **Left** – aligns the left edges of an asset and text element. - **Center** – aligns the center points of an asset and text element. - **Right** – aligns the right edges of an asset and text element. To align an asset, a developer can [enable the *Allow alignment* toggle in the asset field settings](https://docs.prepr.io/content-modeling/field-types#assets-field-settings). 1. On the **Content item** page, hover over an image and click the icon in the top right corner. 2. Select the desired alignment option. ![Align options](https://assets-site.prepr.io/6z6p4evow13e//set-alignment-in-content-item.png) ## Adding captions The *Caption* feature lets you create multiple descriptions for a single asset every time you use this asset in a content item or a content item locale. While the [*Asset Description* field](https://docs.prepr.io/content-management/managing-assets/managing-assets#editing-assets) is recommended for internal use, captions are helpful in the following cases: - When you need to describe a single asset across multiple content items. - When you need to describe a single asset in a multi-language content item. To create a caption for an asset, a developer can [enable the *Allow caption* toggle in the asset field settings](https://docs.prepr.io/content-modeling/field-types#assets-field-settings). 1. On the **Content item** page, hover over the image thumbnail and click the icon in the top right corner, then click **Edit caption**. n ![Edit caption for an asset](https://assets-site.prepr.io/58vqvzy7of20//edit-image-caption-in-content-item.png) 2. In the opened dialog window, type your text, then click **Save** to apply your caption. > **Note:** > > The asset caption is set up per content item locale. Therefore, you can add different captions to each locale of your content item. For more information, see [Manage captions in multi-language content items](https://docs.prepr.io/content-management/localizing-content#manage-captions-in-multi-language-content-items). ## Setting an image focal point An image focal point is the key area that you want to remain visible regardless of how the image is resized. It ensures that important parts of the image (such as a person’s face or a product) stay in focus when displayed across different screen sizes or aspect ratios. If the [set image focal point option is enabled](https://docs.prepr.io/content-modeling/field-types#assets-field-settings), you can choose the focal point for an image when adding or editing the image in a content item. ### Set focal point for new image Follow the same process above to [add images to a content item](#adding-assets-to-a-content-item). When adding an image to a content item and a focal point is required in the [asset field settings](https://docs.prepr.io/content-modeling/field-types#assets-field), you'll see the **Set focal point** button at the bottom of the *Media* dialog window. 1. Click the **Set focal point** button to open the *Set focal point* modal. ![set focal point new image](https://assets-site.prepr.io/2exncwu46o2c//set-focal-point.png) 2. Move the focal point indicator to the part of the image you always want visible and click the **Add 1 asset** button. ### Set focal point for existing image 1. On the **Content item** page, hover over the image and click the icon in the top right corner to open the *Set focal point* modal. 2. Choose the focal point for the selected image and click the **Save** button. ![set focal point gif](https://downloads-site.prepr.io/6v5wa3quil36-setting-focal-point-in-content-item.gif) ## Cropping images *Cropping* an image is important when adding it to your content. You crop images to look great on all devices, such as mobile, desktops, and tablets.\ With Prepr, you can crop image files easily, without altering the original stored files. To enable cropping on images, developers can define the image presets. Check out the [*Assets* field settings](https://docs.prepr.io/content-modeling/field-types#assets-field) for more details. When you add images to content items and crop them according to the presets, a developer can retrieve the chosen presets using the GraphQL API to render them accordingly in the web app. For that, they need to specify a preset name in the API request. For more details, see the sample query for retrieving *Presets* in the [GraphQL API reference](https://docs.prepr.io/graphql-api/schema-field-types#images). Alternatively, a developer can transform images themselves using the API. For more information, check out the [REST API](https://docs.prepr.io/mutation-api/assets-resizing) or the [GraphQL API](https://docs.prepr.io/graphql-api/schema-field-types#images). > **Note:** > > If there are several presets and [cropping is required](https://docs.prepr.io/content-modeling/field-types#assets-field), you must create crops for all of the presets defined for an image. ### Crop new images Follow the same process above to add images to a content item. When adding an image to a content item and cropping is required in the [asset field settings](https://docs.prepr.io/content-modeling/field-types#assets-field), you'll see the **Crop 1 asset** button at the bottom of the *Media* dialog window. ![Crop required - Add new image](https://assets-site.prepr.io/5r5u9uh5akyg//modal-for-required-crop.png) 1. Click the **Crop 1 asset** button to open the crop images window. ![Crop new images in a content item](https://assets-site.prepr.io/2gadzjfyz1yl//crop-preset-examples.png) 2. Simply choose the preset and move the crop area over the part of the image that you want cropped. > **Note:** > > The values you choose is the aspect ratio and is not based on the image resolution. The resolution depends on the uploaded image. 3. If there are multiple presets, simply click the thumbnail of the next preset to add its crop. 4. Once done, click the **Add 1 asset** button to add the cropped image to the content item. ### Crop existing images When cropping is not set to required, you can add a crop after the image has already been added to a content item. To crop an existing image in a content item, complete the following steps for each image: 1. On the **Content item** page, hover over the image and click the icon on the image thumbnail to open the crop editor quickly. Or click the icon in the top right corner, then click **Crop image**. ![Crop image](https://assets-site.prepr.io/2d67uyw3p6o3//crop-existing-image.png) 2. In the crop editor window, select a preset and an area in your crop by moving the entire crop area. 3. Click the **Save** button to apply the updated cropped image. ![Crop existing images in a content item](https://assets-site.prepr.io/4oztcf2s8sjc//crop-modal-for-existing-image.png) ### Edit image crop If you want to change the current crop, complete the following steps for each image: 1. On the **Content item** page, hover over the image and click the icon in the top right corner, then click **Edit crop**. Or simply click the icon to open the crop editor window and follow the same steps as above. ![Edit crop](https://assets-site.prepr.io/25cek9084m4v//edit-crop.png) ## Setting a download filename When you need to provide a download URL link in your content item to make a file available to web app visitors for download, you can set a parameter in the URL to define the filename. For example: `https://acme-company.files.prepr.io/24eovl6bs4q-guide.pdf?filename=the-best-guide-ever.pdf` If you don't set this filename parameter, the filename in the URL is used as is. In the example above, this would be `24eovl6bs4q-guide.pdf`. Source: https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets --- # Managing assets *Learn how to upload, manage and store your assets centrally in Prepr CMS to create photo galleries, video blogs, or even start a live-streaming event in your web application.* - [Introduction to assets](https://docs.prepr.io/content-management/managing-assets/introduction-to-assets): Learn about the asset media types to diversify and enrich your content. - [Managing assets in Media](https://docs.prepr.io/content-management/managing-assets/managing-assets): Learn how to manage your digital assets using an intuitive user interface and the API. - [Using assets in content items](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets): Learn how to configure assets to enhance your web content quality. Source: https://docs.prepr.io/content-management/managing-assets --- # Add Exif data to images automatically *This integration allows you to add Exif data to your images automatically.* ## Introduction Exif (Exchangeable image file format) is a standard that specifies formats for media files. When you activate the *Prepr image processing* integration, you can choose which Exif data to add to your image assets. ## Activate Exif Simply activate the Exif integration with the following steps: ![Activate Exif integration](https://assets-site.prepr.io/fewj9sy7sqb//activate-exif-integration.png) 1. Click the icon and choose the **Integrations** option to view all integrations. Go to the **Prepr image processing** card and click the **Activate** button. 2. Choose the **Exif** option. 3. Select one or more of the fields you want to extract: - **Internal name** - **Description** - **Author** 4. Click the **Save** button. 5. To make any changes to the selected fields, go back to the **Prepr image processing** card and click the **Manage** button to make your changes. Now that the integration with Exif is activated, your chosen fields will be filled automatically whenever new images are uploaded. > **Note:** > > The text values for previously uploaded images will remain unchanged. If you have any questions, please [contact our Support team](https://prepr.io/support). Source: https://docs.prepr.io/integrations/image-processing/exif --- # AI-generate text for images *The AI integration automatically generates text values, such as alt text, when uploading new images.* ## Introduction When you activate the *Prepr image processing* integration, you can choose to integrate to AI to process images. When a user uploads an image, Prepr CMS prompts AI to scan the image and the image file name to detect information and generate selected text values, such as alt text. ## Activate AI generation for image text fields Simply activate the AI integration with the following steps: ![AI text for images integration](https://assets-site.prepr.io/6mz20o1i7isj//activate-ai-integration-for-image-text-values.png) 1. Click the icon and choose the **Integrations** option to view all integrations. Go to the **Prepr image processing** card and click the **Activate** button. 2. Enable the **AI** toggle and choose one or more of the fields listed. - **Internal name** - **Description** - **Custom asset fields** - These include any custom text fields you added to the [*Asset* model](https://docs.prepr.io/content-modeling/defining-the-asset-model#add-fields-to-the-asset-model), like an *Alt text* field. To make changes to the options, go back to the **Prepr image processing** card and click the **Manage** button to make your changes. 3. If you have any custom asset fields in the *Asset* model, set a prompt for each text field you want AI-generated, as follows: - Go to the **Schema** page and click to open the **Asset** model. - Click the text field you want AI-generated and in the dialog, click the *AI* tab. - Then enable the toggle to **Allow AI text generation** - Choose one of the *Suggested prompts* and edit it, if needed, or create your own and click the **Save** button. Once the AI integration is activated and all prompts are set, editors will get generated text for any new images they upload. ![Example image with AI text values](https://downloads-site.prepr.io/4nmdjrif3sqj-generated-ai-text-for-uploaded-image.gif) > **Note:** > > The text values for previously uploaded images will remain unchanged. If you have any questions, please [contact our Support team](https://prepr.io/support). Source: https://docs.prepr.io/integrations/image-processing/ai --- # Prepr image processing Prepr CMS can automatically process images on upload, enriching chosen text values in your image assets to help boost SEO. You can activate this integration to either pull details from Exif data or to let AI generate alt text or other text values for images. - [Exif](https://docs.prepr.io/integrations/image-processing/exif): Automatically populate image text values by extracting relevant Exif data when uploading images. - [AI](https://docs.prepr.io/integrations/image-processing/ai): Automatically populate alt text and other asset fields using AI when uploading images. Source: https://docs.prepr.io/integrations/image-processing --- # Claude Desktop *From this guide, you'll learn how Claude Desktop can work with your Prepr content through the Prepr MCP server, so you can manage content items, assets, and schema using plain-language prompts.* ## Connecting Claude Desktop to the Prepr MCP server Follow the steps below to add Prepr as a custom connector in Claude Desktop, authorize access to your Prepr environment, and test the connection with your first request. ### Open the connector settings In Claude Desktop, open **Customize**, then select **Connectors**. In the search bar, enter **Prepr CMS** and click the **+** button. This opens a browser window to confirm your request. Click the **Continue connecting** button to confirm access to your Prepr environment. ### Authorize access Complete the OAuth flow and select the Prepr environment you want Claude to access. > **Note:** > > The actions Claude can perform depend on your [Prepr user role permissions](https://docs.prepr.io/prepr-mcp-server/authorization#connecting-with-oauth) and the [MCP server permissions](https://docs.prepr.io/prepr-mcp-server/authorization#managing-mcp-server-permissions) of the environment. ### Send your first request To confirm that Claude Desktop can access your Prepr environment, send the following prompt: ```text copy Find the five most recently updated content items in Prepr and show their titles and publication statuses. ``` Claude Desktop can use the tools exposed by the Prepr MCP server to inspect your environment and work with your content. > **Note:** > > The server also inspects the AI context defined in your [environment settings](https://docs.prepr.io/project-setup/setting-up-environments#define-environment-context-for-ai-requests) to interpret requests more accurately. ## What’s next? Explore [typical use cases](https://docs.prepr.io/prepr-mcp-server/use-cases) or review the [available tools and actions](https://docs.prepr.io/prepr-mcp-server/available-tools-and-actions). Source: https://docs.prepr.io/prepr-mcp-server/getting-started/claude-desktop --- # Claude Code *From this guide, you'll learn how Claude Code can work with your Prepr content through the Prepr MCP server, so you can manage content items, assets, and schema using plain-language prompts.* ## Connecting Claude Code to the Prepr MCP server Follow the steps below to add Prepr as a remote HTTP MCP server in Claude Code, authorize access to your Prepr environment, and test the connection from your project with your first request. ### Open your project in Claude Code Choose whether the connection should be available only in the current project or in all your Claude Code projects. Follow Anthropic's [remote HTTP server instructions](https://code.claude.com/docs/en/mcp#option-1-add-a-remote-http-server) for the current command and scope options. ### Add the Prepr MCP server Add a remote HTTP server using: - *Name:* Prepr - *Server URL:* `https://mcp.prepr.io` ### Authorize access Complete the OAuth flow and select the Prepr environment you want Claude Code to access. > **Note:** > > The actions Claude Code can perform depend on your [Prepr user role permissions](https://docs.prepr.io/prepr-mcp-server/authorization#connecting-with-oauth) and the [MCP server permissions](https://docs.prepr.io/prepr-mcp-server/authorization#managing-mcp-server-permissions) of the environment. ### Send your first request To confirm that Claude Code can access your Prepr environment, send the following prompt: ```text copy Find the five most recently updated content items in Prepr and show their titles and publication statuses. ``` Claude Code can use the tools exposed by the Prepr MCP server to inspect your environment and work with your content. > **Note:** > > The server also inspects the AI context defined in your [environment settings](https://docs.prepr.io/project-setup/setting-up-environments#define-environment-context-for-ai-requests) to interpret requests more accurately. ## What’s next? Explore [typical use cases](https://docs.prepr.io/prepr-mcp-server/use-cases) or review the [available tools and actions](https://docs.prepr.io/prepr-mcp-server/available-tools-and-actions). Source: https://docs.prepr.io/prepr-mcp-server/getting-started/claude-code --- # ChatGPT *From this guide, you'll learn how ChatGPT can work with your Prepr content through the Prepr MCP server, so you can manage content items, assets, and schema using plain-language prompts.* > **Note:** > > Custom MCP app availability and setup permissions depend on your ChatGPT plan and workspace role. You may need a workspace admin to enable developer mode or publish the app. ## Connecting ChatGPT to the Prepr MCP server Follow the steps below to add Prepr as a custom MCP app in ChatGPT, authorize access to your Prepr environment, and test the connection with your first request. ### Enable developer mode In ChatGPT, open **Settings**, go to **Apps**, then open **Advanced settings** and enable developer mode. Follow OpenAI's [developer mode and MCP apps guide](https://help.openai.com/en/articles/12584461-developer-mode-and-mcp-apps-in-chatgpt) for current plan availability, workspace permissions, testing, and publishing steps. ### Create a custom app Create a custom MCP app using: - *Name:* Prepr - *Remote MCP server URL:* `https://mcp.prepr.io` ### Authorize access Complete the OAuth flow and select the Prepr environment you want ChatGPT to access. > **Note:** > > The actions ChatGPT can perform depend on your [Prepr user role permissions](https://docs.prepr.io/prepr-mcp-server/authorization#connecting-with-oauth), the [MCP server permissions](https://docs.prepr.io/prepr-mcp-server/authorization#managing-mcp-server-permissions) of the environment, and the actions enabled for the app in ChatGPT. ### Send your first request To confirm that ChatGPT can access your Prepr environment, send the following prompt: ```text copy Find the five most recently updated content items in Prepr and show their titles and publication statuses. ``` ChatGPT can use the tools exposed by the Prepr MCP server to inspect your environment and work with your content. > **Note:** > > The server also inspects the AI context defined in your [environment settings](https://docs.prepr.io/project-setup/setting-up-environments#define-environment-context-for-ai-requests) to interpret requests more accurately. ## What’s next? Explore [typical use cases](https://docs.prepr.io/prepr-mcp-server/use-cases) or review the [available tools and actions](https://docs.prepr.io/prepr-mcp-server/available-tools-and-actions). Source: https://docs.prepr.io/prepr-mcp-server/getting-started/chatgpt --- # Codex *From this guide, you'll learn how Codex can work with your Prepr content through the Prepr MCP server, so you can manage content items, assets, and schema using plain-language prompts.* ## Connecting Codex to the Prepr MCP server Follow the steps below to add Prepr as a streamable HTTP MCP server in Codex, authorize access to your Prepr environment, and test the connection with your first request. ### Open the MCP server settings In the ChatGPT desktop app or Codex IDE extension, open **Settings**, select **MCP servers**, then select **Add server**. Follow OpenAI's [Codex MCP guide](https://developers.openai.com/codex/mcp) for the current desktop app, IDE extension, CLI, and `config.toml` setup options. ### Add the Prepr MCP server Create a streamable HTTP server using: - *Name:* Prepr - *Server URL:* `https://mcp.prepr.io` Save the server, then restart the app or IDE extension when prompted. ### Authorize access Select **Authenticate** for the Prepr server, then complete the OAuth flow and select the Prepr environment you want Codex to access. > **Note:** > > If OAuth isn't available in your setup, configure an [MCP token](https://docs.prepr.io/prepr-mcp-server/authorization#connecting-with-mcp-token) as a bearer token instead. ### Send your first request To confirm that Codex can access your Prepr environment, send the following prompt: ```text copy Find the five most recently updated content items in Prepr and show their titles and publication statuses. ``` Codex can use the tools exposed by the Prepr MCP server to inspect your environment and work with your content. > **Note:** > > The server also inspects the AI context defined in your [environment settings](https://docs.prepr.io/project-setup/setting-up-environments#define-environment-context-for-ai-requests) to interpret requests more accurately. ## What’s next? Explore [typical use cases](https://docs.prepr.io/prepr-mcp-server/use-cases) or review the [available tools and actions](https://docs.prepr.io/prepr-mcp-server/available-tools-and-actions). Source: https://docs.prepr.io/prepr-mcp-server/getting-started/codex --- # Cursor *From this guide, you'll learn how Cursor can work with your Prepr content through the Prepr MCP server, so you can manage content items, assets, and schema using plain-language prompts.* ## Connecting Cursor to the Prepr MCP server Follow the steps below to add Prepr to Cursor's MCP configuration, authorize access to your Prepr environment, and test the connection with your first request. ### Open the MCP settings In Cursor, open **Settings** and go to the MCP configuration area. Follow Cursor's [MCP instructions](https://cursor.com/docs/mcp#static-oauth-for-remote-servers) for the current configuration format and OAuth options. ### Add the Prepr MCP server Create a remote server using: - *Name:* Prepr - *Server URL:* `https://mcp.prepr.io` ### Authorize access Complete the OAuth flow and select the Prepr environment you want Cursor to access. > **Note:** > > The actions Cursor can perform depend on your [Prepr user role permissions](https://docs.prepr.io/prepr-mcp-server/authorization#connecting-with-oauth) and the [MCP server permissions](https://docs.prepr.io/prepr-mcp-server/authorization#managing-mcp-server-permissions) of the environment. ### Send your first request To confirm that Cursor can access your Prepr environment, send the following prompt: ```text copy Find the five most recently updated content items in Prepr and show their titles and publication statuses. ``` Cursor can use the tools exposed by the Prepr MCP server to inspect your environment and work with your content. > **Note:** > > The server also inspects the AI context defined in your [environment settings](https://docs.prepr.io/project-setup/setting-up-environments#define-environment-context-for-ai-requests) to interpret requests more accurately. ## What’s next? Explore [typical use cases](https://docs.prepr.io/prepr-mcp-server/use-cases) or review the [available tools and actions](https://docs.prepr.io/prepr-mcp-server/available-tools-and-actions). Source: https://docs.prepr.io/prepr-mcp-server/getting-started/cursor --- # GitHub Copilot CLI *From this guide, you'll learn how GitHub Copilot CLI can work with your Prepr content through the Prepr MCP server, so you can manage content items, assets, and schema using plain-language prompts.* ## Connecting GitHub Copilot CLI to the Prepr MCP server Follow the steps below to add Prepr to GitHub Copilot CLI's MCP configuration, configure authentication, and test the connection with your first request. ### Open the MCP configuration Open the MCP configuration used by GitHub Copilot CLI. Follow GitHub's [Copilot CLI MCP server instructions](https://docs.github.com/en/copilot/how-tos/copilot-cli/customize-copilot/add-mcp-servers#adding-an-mcp-server) for the current configuration format. ### Add the Prepr MCP server Create a remote server using: - *Name:* Prepr - *Server URL:* `https://mcp.prepr.io` ### Configure authentication Add an Authorization header containing `Bearer YOUR_ACCESS_TOKEN` to the remote HTTP server configuration. > **Note:** > > See [Connecting with an MCP token](https://docs.prepr.io/prepr-mcp-server/authorization#connecting-with-mcp-token) to request a token and configure token-based access. ### Send your first request To confirm that GitHub Copilot CLI can access your Prepr environment, send the following prompt: ```text copy Find the five most recently updated content items in Prepr and show their titles and publication statuses. ``` GitHub Copilot CLI can use the tools exposed by the Prepr MCP server to inspect your environment and work with your content. > **Note:** > > The server also inspects the AI context defined in your [environment settings](https://docs.prepr.io/project-setup/setting-up-environments#define-environment-context-for-ai-requests) to interpret requests more accurately. ## What’s next? Explore [typical use cases](https://docs.prepr.io/prepr-mcp-server/use-cases) or review the [available tools and actions](https://docs.prepr.io/prepr-mcp-server/available-tools-and-actions). Source: https://docs.prepr.io/prepr-mcp-server/getting-started/github-copilot --- # Notion custom agent *From this guide, you'll learn how a Notion custom agent can work with your Prepr content through the Prepr MCP server, so you can manage content items, assets, and schema using plain-language prompts.* ## Connecting a Notion custom agent to the Prepr MCP server Follow the steps below to add Prepr as an MCP connection for a Notion custom agent, authorize access to your Prepr environment, and test the connection with your first request. ### Open your custom agent In Notion, open the custom agent that you want to connect to Prepr and go to its MCP connection settings. Follow Notion's [custom agent MCP instructions](https://www.notion.com/help/mcp-connections-for-custom-agents#how-to-add-a-custom-mcp-connection) for the current admin and agent setup steps. > **Note:** > > A workspace admin must enable custom MCP servers before you can add the connection. The admin may also need to approve the Prepr MCP server URL. ### Add the Prepr MCP server Create a custom MCP connection using: - *Name:* Prepr - *Server URL:* `https://mcp.prepr.io` ### Authorize access Complete the OAuth flow and select the Prepr environment you want the custom agent to access. > **Note:** > > The actions the agent can perform depend on your [Prepr user role permissions](https://docs.prepr.io/prepr-mcp-server/authorization#connecting-with-oauth) and the [MCP server permissions](https://docs.prepr.io/prepr-mcp-server/authorization#managing-mcp-server-permissions) of the environment. ### Send your first request To confirm that your Notion custom agent can access your Prepr environment, send the following prompt: ```text copy Find the five most recently updated content items in Prepr and show their titles and publication statuses. ``` Your Notion custom agent can use the tools exposed by the Prepr MCP server to inspect your environment and work with your content. > **Note:** > > The server also inspects the AI context defined in your [environment settings](https://docs.prepr.io/project-setup/setting-up-environments#define-environment-context-for-ai-requests) to interpret requests more accurately. ## What’s next? Explore [typical use cases](https://docs.prepr.io/prepr-mcp-server/use-cases) or review the [available tools and actions](https://docs.prepr.io/prepr-mcp-server/available-tools-and-actions). Source: https://docs.prepr.io/prepr-mcp-server/getting-started/notion-custom-agent --- # OpenCode *From this guide, you'll learn how OpenCode can work with your Prepr content through the Prepr MCP server, so you can manage content items, assets, and schema using plain-language prompts.* ## Connecting OpenCode to the Prepr MCP server Follow the steps below to add Prepr to OpenCode's MCP configuration, configure authentication, and test the connection with your first request. ### Open the MCP configuration Open the configuration file or settings area where OpenCode manages MCP servers. Follow the official [OpenCode MCP server guide](https://opencode.ai/docs/mcp-servers/) for the current remote server configuration and OAuth flow. ### Add the Prepr MCP server Create a remote server using: - *Name:* Prepr - *Server URL:* `https://mcp.prepr.io` ### Configure authentication Complete the OAuth flow when OpenCode prompts you. You can also start it manually using `opencode mcp auth prepr`. If OAuth isn't available in your setup, add an Authorization header containing `Bearer YOUR_ACCESS_TOKEN`. > **Note:** > > See [Connecting with an MCP token](https://docs.prepr.io/prepr-mcp-server/authorization#connecting-with-mcp-token) to request a token and configure token-based access. ### Send your first request To confirm that OpenCode can access your Prepr environment, send the following prompt: ```text copy Find the five most recently updated content items in Prepr and show their titles and publication statuses. ``` OpenCode can use the tools exposed by the Prepr MCP server to inspect your environment and work with your content. > **Note:** > > The server also inspects the AI context defined in your [environment settings](https://docs.prepr.io/project-setup/setting-up-environments#define-environment-context-for-ai-requests) to interpret requests more accurately. ## What’s next? Explore [typical use cases](https://docs.prepr.io/prepr-mcp-server/use-cases) or review the [available tools and actions](https://docs.prepr.io/prepr-mcp-server/available-tools-and-actions). Source: https://docs.prepr.io/prepr-mcp-server/getting-started/opencode --- # Get started with the Prepr MCP server *Connect your preferred AI client to the Prepr MCP server, then test the connection with your Prepr content.* ## Before you start All clients connect to the same remote MCP server: - *Server URL:* `https://mcp.prepr.io` - **Authentication:** OAuth, when supported by the client > **Note:** > > If your AI client doesn't support OAuth, you also need to provide an [Authorization header](https://docs.prepr.io/prepr-mcp-server/authorization#connecting-with-mcp-token): `Bearer YOUR_ACCESS_TOKEN`, where `YOUR_ACCESS_TOKEN` is your MCP server token. > Contact [Prepr Support](mailto:support@prepr.io?subject=Request%20Prepr%20MCP%20token) to request an MCP token for your Prepr environment. ## Choose your AI client - [Claude Desktop](https://docs.prepr.io/prepr-mcp-server/getting-started/claude-desktop): Connect Prepr as a custom connector in Claude Desktop. - [Claude Code](https://docs.prepr.io/prepr-mcp-server/getting-started/claude-code): Add Prepr as a remote HTTP MCP server in Claude Code. - [ChatGPT](https://docs.prepr.io/prepr-mcp-server/getting-started/chatgpt): Add Prepr as a custom MCP app in ChatGPT. - [Codex](https://docs.prepr.io/prepr-mcp-server/getting-started/codex): Connect Prepr to Codex as an MCP server. - [Cursor](https://docs.prepr.io/prepr-mcp-server/getting-started/cursor): Add the Prepr MCP server to Cursor. - [GitHub Copilot CLI](https://docs.prepr.io/prepr-mcp-server/getting-started/github-copilot): Configure the Prepr MCP server for GitHub Copilot CLI. - [Notion custom agent](https://docs.prepr.io/prepr-mcp-server/getting-started/notion-custom-agent): Add Prepr as an MCP connection for a Notion custom agent. - [OpenCode](https://docs.prepr.io/prepr-mcp-server/getting-started/opencode): Connect OpenCode to the Prepr MCP server. Review the [available tools and actions](https://docs.prepr.io/prepr-mcp-server/available-tools-and-actions) and [typical use cases](https://docs.prepr.io/prepr-mcp-server/use-cases) before you start building workflows. Source: https://docs.prepr.io/prepr-mcp-server/getting-started --- # Introduction to the Next.js complete guide *This section introduces you to the complete guide that shows you how to connect Prepr to a Next.js project including styling, adaptive content, and A/B testing and a preview bar.* At the end of the complete guide, you’ll have a working website with personalization and A/B testing like the images below. You can customize this project to fit the requirements for your web app. **Home page for general website visitors** \ ![Dynamic home page](https://assets-site.prepr.io/p6rja5r1cj8//dynamic-website-with-prepr-content.png) **Two variants of a running A/B test on a landing page**\ ![Electric landing page with A/B test](https://assets-site.prepr.io/2u8iwxsr0nls//a-b-test-on-home-page.png) **Personalized home page for electric car lovers**\ ![Personalized home page](https://assets-site.prepr.io/50el53oh29zt//personalized-home-page-electric-lease.png) > **Note:** > > If you can't wait and want to skip ahead, clone the [repository on GitHub](https://github.com/preprio/next-complete-starter) to run the demo locally or visit our [demo website](https://acme-lease.prepr.io/) to see it in action. > These resources and this guide are based on the latest version of Next.js. ## Prerequisites Make sure the following is in place before getting started: - [A free Prepr account](https://signup.prepr.io/?plan=free) - [An environment with Acme Lease demo data in Prepr](https://docs.prepr.io/project-setup/setting-up-environments#create-an-environment) - [The latest version of Node.js](https://nodejs.org/en/download) Now that you know what to expect and have your Prepr environment ready, you can get started with the first step to [set up the Next.js project](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-1-set-up-a-nextjs-project). Source: https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/introduction --- # Set up a Next.js project *The instructions below will guide you through the first step of the Next.js complete guide. These show you how to create a Next.js website with static components.* ![Next.js site with static components](https://assets-site.prepr.io/6e4vb1yxyp5z//static-website.png) *Next.js website with static text and images.* You can also watch the video for step-by-step instructions detailed in the guide below.\ \ [Watch video](https://player.mux.com/YUtNmRzNWzFM2Q5pNqhCD37FNTnjC6n3XZek2G99Y9A) ## Create a Next.js website with static components If you have an existing Next.js project then you can skip this section and continue with the next section to [make your Next.js project dynamic with Prepr content](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-2-make-the-project-dynamic). Otherwise, let's get started. ### Install a Next.js project 1. Open a terminal, go to your development projects folder and execute the command below to create a new Next.js project called `nextjs-complete-starter`. ```bash copy npx create-next-app@latest nextjs-complete-starter ``` Choose `No, customize settings` and mark the options as shown in the image below to create the same project structure used in this guide. ![options when installing Next.js](https://assets-site.prepr.io/1y6x5suju59p//install-nextjs-project.png) 2. When the project is successfully created, go to the `nextjs-complete-starter` folder, the root directory of the project, and run the project with the following commands in the terminal: ```bash copy cd nextjs-complete-starter ``` ```bash copy npm run dev ``` 3. You should now be able to view your app on your localhost, for example, `http://localhost:3000/`. ### Update styling Follow the steps below to add some simple styling to the project. 1. In the terminal, stop the server you started in the above step (`CTRL-C`) and while in your project `nextjs-complete-starter` directory, install an additional package to make styling easier in the components you'll create below. ```bash copy npm install classnames ``` 2. Open the Next.js project with your preferred code editor. > **Note:** > > **Troubleshooting** \ > > If you don't have a `src` folder in your project, it means you selected `No` to the *Would you like your code inside a `src/` directory?* question in the step above. > You can either reinstall the Next.js project, or when you come across `src` in the rest of the instructions in this guide, use the root directory instead. 3. Replace the styling in the `globals.css` file in the `app` folder to import the Tailwind CSS and to set up some additional colors and spacing: ```css filename="./src/app/globals.css" copy @import "tailwindcss"; @theme { --font-size-mb-4xl: 2rem; --font-size-mb-5xl: 2.675rem; --spacing-8xl: 90rem; --container-8xl: 90rem; --container-prose: 50rem; --color-gray-50: #EEEEEE; --color-primary-50: #EFF6FF; --color-primary-100: #DBEAFE; --color-primary-600: #2563EB; --color-primary-700: #1D4ED8; --color-secondary-500: #4748B; --color-secondary-700: #334155; } ``` ### Add a simple navigation bar 1. Create a `components` folder in the `src` directory and add the following three components to your Next.js project to display a navigation bar at the top of your website. **Button** ```tsx filename="./src/components/button.tsx" copy import classNames from 'classnames' export interface ButtonProps extends React.ButtonHTMLAttributes { children: React.ReactNode buttonStyle?: 'primary' | 'secondary' className?: string } export default function Button(props: ButtonProps) { const { children, buttonStyle = 'primary', className, ...additionalProps } = props const classes = classNames( 'flex rounded-full text-sm px-6 py-2.5 font-medium', buttonStyle === 'primary' && 'bg-primary-600 hover:bg-primary-700 text-white', buttonStyle === 'secondary' && 'bg-white hover:bg-gray-50 text-secondary-700', className ) return ( ) } ``` **Logo** ```tsx filename="./src/components/logo.tsx" copy interface LogoProps { text?: boolean } export default function Logo({ text = true }: LogoProps) { if (text) { return ( ) } else { return ( ) } } ``` **Navigation bar** ```tsx filename="./src/components/navbar.tsx" copy 'use client' import Logo from '@/components/logo' import Link from 'next/link' import Button from "@/components/button"; export default function NavBar() { const navItems = [ { text: 'Find a car', href: '#', }, { text: 'About Acme', href: '#', }, { text: 'Blog', href: '#', }, { text: 'Contacts', href: '#', }, { text: , href: '#', }, ] return (
) } ``` Before you can view the navigation bar, you need to include it in the project layout. ### Update the project layout Follow the steps below to update the project layout to include the navigation bar and use Tailwind classes for styling. 1. Go to the `layout.tsx` file in the `src/app` folder and remove the default code and replace it with the simplified layout below. ```tsx filename="./src/app/layout.tsx" copy import type {Metadata} from "next"; import NavBar from "@/components/navbar"; import './globals.css' import {Ubuntu} from "next/font/google"; const ubuntu = Ubuntu({weight: ['400', '700'], subsets: ['latin']}) export const metadata: Metadata = { title: "Prepr Next.js complete starter", description: "Showing the power of personalization and A/B testing", }; export default async function RootLayout({children,}: {children: React.ReactNode}){ return ( {children} ); } ``` 2. In the `src/app` folder, replace the code in the `page.tsx` file with the following code to display your page. ```tsx filename="./src/app/page.tsx" copy export default function Page() { return (

My home page

); } ``` 3. Restart the localhost server with `npm run dev` in the terminal and go back to your browser. You should see the navigation bar and your heading text like in the image below. ![Update page with navigation bar](https://assets-site.prepr.io//4sjn2vin77t0-navigation-bar.png) Once your Next.js project is created and working, you can create a static page with components. ### Create static components In this step, you'll create a static version of the page including two front-end components. One for a *Hero* section at the top of the page and another for *Feature* sections to highlight features for the company. Add these components to the front end as follows: 1. Go to the `src/app` folder and create a new folder `[[...slug]]` and move the `page.tsx` file to this folder. This new folder creates a [dynamic route](https://nextjs.org/docs/pages/building-your-application/routing/dynamic-routes) to all your pages. 2. Refresh your page in the browser to check if your website still works. > **Note:** > > **Troubleshooting** \ > > **Error**: `You cannot define a route with the same specificity as a optional catch-all route ...`\ > > **Cause**: You have two `page.tsx` files in your project. \ > > **Solution**: Make sure the original `page.tsx` file is removed from the `src/app` folder when you move it to the new `[[...slug]]` folder. 3. Replace the simple code in the `page.tsx` with the code below to display the components with static data. ```js filename="./src/app/[[...slug]]/page.tsx" copy // Import the components to display them import HeroSection from "@/components/sections/hero-section"; import FeatureSection from "@/components/sections/feature-section"; export default function Page() { return (
); } ``` > **Note:** > > You'll notice compilation errors in this page because the components don't exist yet. Continue the next steps to create these components and resolve these errors. 4. In the `src/components` folder, create a new folder called `sections`. In this new folder, create new files called `hero-section.tsx` and `feature-section.tsx` and copy the code below to construct these components. **Hero section** ```tsx filename="./src/components/sections/hero-section.tsx" copy import Link from 'next/link' import Button from '@/components/button' export default function HeroSection() { return (

Acme Lease

A car leasing company

) } ``` **Feature section** ```tsx filename="./src/components/sections/feature-section.tsx" copy import Link from "next/link"; import Button from '@/components/button' export default function FeatureSection({align = 'left'}: {align?: 'left' | 'right'}) { const image = (
) return (
{align === 'left' && image}

Business Section

All about business

{align === 'right' && image}
) } ``` When you go back to your page in the browser, you should now see something like the image below. ![Next.js site with static components](https://assets-site.prepr.io/6e4vb1yxyp5z//static-website.png) Great work on getting your static web app working! Continue your journey to the next section to [make your Next.js project dynamic with Prepr content](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-2-make-the-project-dynamic). Source: https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-1-set-up-a-nextjs-project --- # Make your Next.js project dynamic *This guide shows you how to connect an existing Next.js project to Prepr to retrieve and show Acme Lease demo content.* ## Connect your Next.js website to Prepr The steps below continue from the previous section, [Set up a Next.js project](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-1-set-up-a-nextjs-project). If you don't yet have a Next.js website with static pages, follow the steps in this section first. Otherwise, let's get started. ### Install Apollo Client Apollo Client is an integration tool that helps to retrieve CMS data with GraphQL. The instructions below show you how to install Apollo Client so that you can add GraphQL queries to request data from the Prepr API. 1. Stop the localhost website server (`CTRL-C`) if it's running and execute the following command in the terminal: ```bash copy npm add @apollo/client @apollo/client-integration-nextjs @graphql-typed-document-node/core graphql ``` 2. Once done, create a file called `apollo-client.ts` in the `src` folder. Copy the following code to this file to import and initialize the Apollo client: ```ts filename="./src/apollo-client.ts" copy import {HttpLink} from "@apollo/client"; import { ApolloClient,InMemoryCache, registerApolloClient, } from "@apollo/client-integration-nextjs"; export const {getClient, query, PreloadQuery} = registerApolloClient(() => { return new ApolloClient({ cache: new InMemoryCache(), link: new HttpLink({ // this needs to be an absolute url, as relative urls cannot be used in SSR uri: process.env.PREPR_GRAPHQL_URL, // you can disable result caching here if you want to // (this does not work if you are rendering your page with `export const dynamic = "force-static"`) // fetchOptions: { cache: "no-store" }, }), }); }); ``` This client will be used to make API requests to endpoints provided by the Prepr CMS across your Next.js application. 3. We recommend using environment variables to store sensitive information like access tokens. To add environment variables, create a `.env` file in the root directory of your project and add the access token like this: ```bash filename="./.env" copy PREPR_GRAPHQL_URL= ``` 4. Replace the placeholder value `` with the *API URL* of an access token from Prepr. Get an access token by logging into your Prepr account:\ a. Click the icon and choose the **Access tokens** option to view all the access tokens.\ b. Click to open the *GraphQL Production* access token, and copy the *API URL* to only retrieve published content items on your site for now. c. Paste the copied *API URL* in your `.env` file. > **Note:** > > Use the *GraphQL Production* access token to request published content items for your live app and use the *GraphQL Preview* token to make a [preview of unpublished content items](https://docs.prepr.io/project-setup/setting-up-previews-and-visual-editing) for your content editors. ![GraphQL production access token example](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) 5. Execute the following command to restart the server and refresh your page in the browser. ```bash copy npm run dev ``` If your website runs without errors, then the setup above was done correctly. Now that the Apollo client is installed, you can retrieve content from Prepr. Before we create a query to retrieve content, let's follow some best practices for TypeScript and install a GraphQL code generator. ### Install a code generator In this step, you'll install a GraphQL code generator to generate TypeScript types automatically for your queries and keep these up to date with any schema changes. Check out the [TypeScript best practices doc](https://docs.prepr.io/development/best-practices/typescript#install-a-graphql-code-generator) for more details. In the meantime, follow the steps below to install the GraphQL code generator. 1. Stop the localhost website server (`CTRL-C`) if it's running and execute the following commands in your terminal: ```bash copy npm add -D @graphql-codegen/cli @graphql-codegen/client-preset @graphql-codegen/introspection npm add dotenv ``` 2. Once done, create a new file, `codegen.ts` in the root of your project. Update it with the code below. ```ts filename="./codegen.ts" copy import "dotenv/config"; import type { CodegenConfig } from "@graphql-codegen/cli"; const config: CodegenConfig = { overwrite: true, schema: process.env.PREPR_GRAPHQL_URL, documents: ["src/queries/**/*.graphql"], generates: { "src/gql/": { preset: "client", presetConfig: { fragmentMasking: false }, }, "./graphql.schema.json": { plugins: ["introspection"] }, }, }; export default config; ``` 3. To complete the codegen installation, make sure the highlighted lines below are in your `package.json`. ```json copy {7-9} { "name": "nextjs-complete-starter", "version": "0.1.0", "private": true, "scripts": { "dev": "next dev", "build": "npm run codegen && next build", "start": "next start", "codegen": "graphql-codegen —-watch" }, ... } ``` 4. Execute the following command to restart the server and refresh your page in the browser to make sure the Apollo client is installed correctly: ```bash copy npm run dev ``` If your website runs without errors, then the setup above was done correctly. Now you can add a query to your project. ### Add a GraphQL query Once the GraphQL code generator is installed, you can add a query to get the content for your home page from Prepr. If you’re using preloaded demo data in your Prepr environment as mentioned in the [Prerequisites](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/introduction#prerequisites), you should have a *Homepage* content item like in the images below. The *Homepage* has all the elements of the page in a [*Stack* field](https://docs.prepr.io/content-modeling/field-types#stack-field). The stack makes it easy for an editor to set up their page content in a flexible structure for the front end. ![Home page content item - stack field](https://assets-site.prepr.io/3e0ain6d3kyj//home-page-content-item-highlight-stack-field.png) > **Note:** > > You can see that the hero and feature sections are in adaptive content blocks. We'll explain that in more detail in the upcoming personalization chapter. Follow the steps below to add a query to retrieve the id, title, slug, and a stack containing the hero and feature sections with their own fields: 1. Create a `queries` folder in the `src` directory of your project and create a file named `get-page-by-slug.graphql`. 2. Add the following queries in the `queries` folder to retrieve a page by its slug: > **Note:** > > You'll notice that we've created separate fragment queries for the button, hero and feature UI components in a `fragments` folder. > Fragments in GraphQL are a way to define a set of fields that can be reused. **Page query** ```graphql filename="./src/queries/get-page-by-slug.graphql" copy # The GetPageBySlug query uses a slug value from the input parameter to retrieve a specific page query GetPageBySlug($slug: String) { Page(slug: $slug) { title _id content { __typename ... on Hero { ...Hero } ... on Feature { ...Feature } } } } ``` **Button fragment query** ```graphql filename="./src/queries/fragments/button.fragment.graphql" copy fragment Button on Button { button_type text external_url link { ... on Category { _slug } ... on Page { _slug } ... on Post { _slug } } } ``` **Feature fragment query** ```graphql filename="./src/queries/fragments/feature.fragment.graphql" copy fragment Feature on Feature { _id heading sub_heading button { ...Button } _context { variant_key } image_position image { url(width: 870, height: 570) } } ``` **Hero fragment query** ```graphql filename="./src/queries/fragments/hero.fragment.graphql" copy fragment Hero on Hero { _id sub_heading image { url(preset: "Hero", width: 2000) height width } _context { variant_key } heading buttons { ...Button } } ``` > **Note:** > > You can create and [test GraphQL queries](https://docs.prepr.io/graphql-api/test-queries) using the [Apollo explorer](https://studio.apollographql.com/sandbox/explorer) from Prepr. > Open the **API Explorer** from the *Homepage* content item or from your access token page. ### Generate TypeScript types 1. Now that the query is created, generate the TypeScript types by running the following command in the terminal: ```bash copy npm run codegen ``` 2. If the command is successful you'll see a new file called `graphql.ts` in the `gql` folder. Check out the [TypeScript doc](https://docs.prepr.io/development/best-practices/typescript#generate-typescript-types) for more details. 3. Test the query by updating the `page.tsx` file. The code below executes the query and prints the query results to the console. ```tsx filename="./src/app/[[...slug]]/page.tsx" copy {4-6, 8-10, 12-26, 28-43} // Import the components to display them import HeroSection from "@/components/sections/hero-section"; import FeatureSection from "@/components/sections/feature-section"; import {getClient} from "@/apollo-client"; import {GetPageBySlugDocument, GetPageBySlugQuery} from "@/gql/graphql"; import {notFound} from "next/navigation"; {/* Set up SSR rendering strategy, override default static rendering */} export const revalidate = 0 export const dynamic = 'force-dynamic' async function getData(slug: string) { const {data} = await getClient().query({ query: GetPageBySlugDocument, variables: { slug: slug, }, // Define Apollo cache strategy to ensure SSR fetchPolicy: 'no-cache', }) if (!data) { return notFound() } return data } // Include parameters to check the slug in the URL export default async function Page({params}: { params: { slug: string | string[] } }) { let {slug} = await params // Set the slug to the home page slug if there's no slug if (!slug) { slug = '/' } // Add a forward slash to the slug to navigate to the correct page. if (slug instanceof Array) { slug = slug.join('/') } const data = await getData(slug) console.log(JSON.stringify(data.Page, undefined, 2)) return (
); } ``` When you check the console in your terminal, the response to the query looks something like this: ![query results in console](https://assets-site.prepr.io/2yaft8ezrpeo//query-output.png) Now that the query has been created and retrieves the data successfully, fetch and show the specific page content from the query results. > **Note:** > > **Troubleshooting** \ > > **Compilation error**: In the `page.tsx` for the line `import {GetPageBySlugDocument, GetPageBySlugQuery} from "@/gql/graphql"`: `'"@/gql/graphql"' has no exported member named 'GetPageBySlugDocument'`.\ > > **Cause**: The query name was not defined or is different than expected in the `page.tsx`.\ > > **Solution**: Check that the [query you created](#add-a-graphql-query) has a query name. In our example the query name is defined as `query GetPageBySlug(...){...}`. After updating the query, [regenerate the TypeScript types](#generate-typescript-types). ### Fetch page content To view the Prepr content in your website, you need to fetch the page elements. As mentioned previously, the page has elements in a *Stack* field. You'll fetch two components, a *Hero* section and *Feature* sections in the stack. 1. The components also include image content so you first need to set up your project to use the Next.js *Image* component by adding the image domain to the `next.config.ts` with the following code: ```ts filename="./next.config.ts" copy {4-11} import type { NextConfig } from "next"; const nextConfig: NextConfig = { /* config options here */ images: { remotePatterns: [ { hostname: 'demo-patterns.stream.prepr.io', }, ], }, }; export default nextConfig; ``` > **Note:** > > The `hostname` matches the domain of the demo images included with the demo content you loaded in your environment. > When you add your own images and other assets, don't forget to change the `hostname` in the Next.js configuration to match the domain of your own images. 2. Next, update your components to display data dynamically. **Hero section** ```tsx filename="./src/components/sections/hero-section.tsx" copy {2-3, 6-8, 15, 18, 28} import Link from 'next/link' import { HeroFragment } from '@/gql/graphql' import Image from 'next/image' import Button from '@/components/button' export default function HeroSection({ item }: { item: HeroFragment }) { const image = item.image return (

{item.heading}

{item.sub_heading}

Hero Image
) } ``` **Feature section** ```tsx filename="./src/components/sections/feature-section.tsx" copy {2-3, 6-12, 18, 21, 24, 28, 32} import Link from "next/link"; import { FeatureFragment } from '@/gql/graphql' import Image from 'next/image' import Button from '@/components/button' export default function FeatureSection({ item }: { item: FeatureFragment }) { const button = item.button const image = (
Feature image
) return (
{item.image_position === 'LEFT' && image}

{item.heading}

{item.sub_heading}

{item.image_position === 'RIGHT' && image}
) } ``` 3. Now you can fetch and set the data for the page by getting elements from the stack and returning them in the `page.tsx` file as follows: ```tsx filename="./src/app/[[...slug]]/page.tsx" copy {42-49, 53} // Import the section components to display them import HeroSection from "@/components/sections/hero-section"; import FeatureSection from "@/components/sections/feature-section"; import {getClient} from "@/apollo-client"; import {GetPageBySlugDocument, GetPageBySlugQuery} from "@/gql/graphql"; import {notFound} from "next/navigation"; export const revalidate = 0 export const dynamic = 'force-dynamic' async function getData(slug: string) { const {data} = await getClient().query({ query: GetPageBySlugDocument, variables: { slug: slug, }, fetchPolicy: 'no-cache', }) if (!data.Page) { return notFound() } return data } export default async function Page({ params}: {params: Promise<{ slug: string | string[]}>}) { let { slug} = await params // Set the slug to the home page value if there's no slug if (!slug) { slug = '/' } // Add a forward slash to the slug to navigate to the correct page. if (slug instanceof Array) { slug = slug.join('/') } const data = await getData(slug) const elements = data.Page?.content.map((element, index) => { if (element.__typename === 'Feature') { return } else if (element.__typename === 'Hero') { return } }) return (
{elements}
); } ``` > **Note:** > > In later chapters, this guide shows you how to implement Personalization and A/B testing which are dynamic features in Prepr. > These features work best with[SSR (Server-side rendering)](https://docs.prepr.io/development/best-practices/csr-ssr-ssg#server-side-rendering-ssr). > In the code above, take note of the caching setup for Next.js and Apollo to implement SSR. > > Check out the [caching strategies doc](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/caching-strategies) for more details. > > If you've enabled `cacheComponents` in Next.js 16, remove the `revalidate` and `dynamic` exports from the code above and wrap the page content in a `` boundary. > Check out the [Cache Components section](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/caching-strategies#cache-components) for more details. Now when you view the website on your localhost, you'll see something like the image below: ![Dynamic home page](https://assets-site.prepr.io/p6rja5r1cj8//dynamic-website-with-prepr-content.png) > **Note:** > > **Troubleshooting** \ > > **Compilation error**: Line `import {FragmentType, getFragmentData} from "@/gql";` in the components `hero-section.tsx` and `feature-section.tsx` -> `'"@/gql"' has no exported member named 'FragmentType'.`\ > > **Cause**: The `codegen.ts` does not have preset definition for query fragments.\ > > **Solution**: [Update the `codegen.ts`](#install-a-code-generator) to include the presets for fragments and [regenerate the TypeScript types](#generate-typescript-types). Congratulations! You have successfully connected your front end to Prepr to make your website dynamic. Continue your journey to the next section to [set up data collection](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-3-set-up-data-collection). Source: https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-2-make-the-project-dynamic --- # Set up data collection with tracking and the Prepr Toolkit *Prepr is a CMS that includes built-in A/B testing and personalization features. These capabilities require visitor data to measure your test results and to build segments for personalization. This chapter of the Next.js complete guide shows you how to enable tracking to collect this data in your Next.js front end and how to simplify this data collection with the Prepr Toolkit.* The steps below continue from the previous section, [Make your Next.js project dynamic](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-2-make-the-project-dynamic). If you don't yet have a Next.js project connected to Prepr, follow the previous steps listed in the [Complete guide overview](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide) to create one. Otherwise, let's get started. ## Install the Prepr Toolkit We developed the [Prepr Toolkit](https://github.com/preprio/prepr-toolkit) to simplify working with event data for A/B testing and personalization. In the steps below, you'll enable tracking in your website by adding the *Prepr Tracking Code* to your front end. This code generates the `__prepr_uid` cookie for each website visitor. This visitor gets stored in Prepr with this unique ID, if they don't already exist. This means you can use this `__prepr_uid` cookie value to set the API request header, `Prepr-Customer-Id`, when you retrieve the page content for A/B testing and personalization. The *Prepr Toolkit* prepares the `Prepr-Customer-Id` API request header for you, using the `__prepr_uid` cookie value. Based on this value, Prepr retrieves the right content as follows: - A/B testing: Prepr decides which variant, A or B, to return in the content. - Personalization: Prepr checks the segment that this visitor belongs to and retrieves the matching personalized variant. To install the *Prepr Tookit*, follow the steps below. 1. In your Next.js project, stop the localhost website server (`CTRL-C`) if it's running and install the package with the following terminal command: ```bash copy npm install @preprio/toolkit ``` Once done, you need to call a function in the package to prepare the API request headers before the page gets rendered in your website. 2. To do this, go to your project and create a new file `proxy.ts` in the `src` folder. Then add the following code to perform the package's preprocessing logic on user requests. ```ts filename="./src/proxy.ts" copy import type { NextRequest } from 'next/server' import { createPreprMiddleware } from '@preprio/toolkit/nextjs' export function proxy(request: NextRequest) { return createPreprMiddleware(request, { preview: false }) } ``` ## Enable Prepr tracking and collect view events ### Enable Prepr tracking When you enable Prepr tracking in your front end, it captures visitor data and lets you track how they engage with your content. Follow the steps below to enable Prepr tracking: 1. Go to the `src/app` folder and add the highlighted code below to the `layout.tsx` file. ```js filename="./src/app/layout.tsx" copy {6-7,18-19, 23-25} import type {Metadata} from "next"; import NavBar from "@/components/navbar"; import './globals.css' import {Ubuntu} from "next/font/google"; // Helper function to get access token and PreprTrackingPixel import import { extractAccessToken, PreprTrackingPixel } from '@preprio/toolkit/nextjs' const ubuntu = Ubuntu({weight: ['400', '700'], subsets: ['latin']}) export const metadata: Metadata = { title: "Prepr Next.js complete starter", description: "Showing the power of personalization and A/B testing", }; export default async function RootLayout({children,}: {children: React.ReactNode}) { // Get access token from the environment variable const accessToken = extractAccessToken(process.env.PREPR_GRAPHQL_URL!) return ( {accessToken && } {children} ); } ``` > **Note:** > > To see the tracking pixel value, in your Prepr environment, click the icon and choose the **Event management** option. > Click the **Get tracking code** link to view the *Prepr Tracking Code*. > > ![event tracking page](https://assets-site.prepr.io/2ovyjc1jscr8//updated-event-tracking-screenshot.png) 2. Refresh your website page in the browser. If there are no errors in the terminal console, Prepr tracking is set up successfully. ### Track page view events For the subsequent A/B testing and personalization sections in this guide, we want to track page view events. To do this, add a content item meta property to the `page.tsx` file like in the highlighted code below. The tracking pixel automatically recognizes this property and records the view event in Prepr. Check out the [data collection events docs](https://docs.prepr.io/data-collection/recording-events) for more details. ```tsx filename="./src/app/[[...slug]]/page.tsx" copy {52} async function getData(slug: string) { const {data} = await getClient().query({ query: GetPageBySlugDocument, variables: { slug: slug, }, fetchPolicy: 'no-cache', }) if (!data.Page) { return notFound() } return data } { let { slug} = await params // Set the slug to the home page value if there's no slug if (!slug) { slug = '/' } // Add a forward slash to the slug to navigate to the correct page. if (slug instanceof Array) { slug = slug.join('/') } const data = await getData(slug) const elements = data.Page?.content.map((element, index) => { if (element.__typename === 'Feature') { return } else if (element.__typename === 'Hero') { return } }) return (
{elements}
); } ``` > **Note:** > > **Using Cache Components?** If you've enabled `cacheComponents` in Next.js 16, remove the `revalidate` and `dynamic` exports from the code above and wrap the page content in a `` boundary. > Check out the [caching strategies doc](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/caching-strategies#cache-components) for more details. For more details on how to track other types of events, check out the [data collection docs](https://docs.prepr.io/data-collection/recording-events). ### Test data collection You can easily check if the data collection is successful with the following steps. 1. Go to your website in the browser and refresh the page. 2. In your Prepr environment, go to the **Segments** page. 3. If the page view is recorded successfully you'll see a recent visitor in the **All visitors** list. 4. Click to open this visitor and you should see the *View* event on the *Homepage* similar to the image below. ![page view event](https://assets-site.prepr.io/3im9gynz7ubp//page-view-events.png) Congratulations! You've successfully enabled Prepr tracking in your Next.js front end, started collecting page view events, and installed the Prepr Toolkit for A/B testing and personalization. Now, you're ready to [add A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-4-add-ab-testing) and [personalization](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-5-add-personalization) to your website. Source: https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-3-set-up-data-collection --- # Add A/B testing to your Next.js website *Prepr CMS enables editors to create A/B tests directly within their content. This means testing different content versions is simple and effective. This chapter of the Next.js complete guide demonstrates how to set up A/B testing in Prepr, including testing the display of variants and measuring the results.* At the end of this section, you'll see the A and B versions on the *Electric Lease Landing Page*. ![Electric landing page with A/B test](https://assets-site.prepr.io/2u8iwxsr0nls//a-b-test-on-home-page.png) The steps below make use of the A/B test in the *Electric Lease Landing Page* content item from the Acme Lease demo data. ![A/B test in Electric lease content item](https://assets-site.prepr.io/6ox5hri28pd6//electric-lease-landing-page.png) This chapter continues from the previous section, [Set up data collection](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-3-set-up-data-collection). If you haven't yet enabled Prepr tracking in your Next.js project, follow the steps in this section first. Otherwise, let's get started. ## Set up A/B testing for the *Electric Lease Landing Page* You can run an A/B test on parts of a web page to show two different versions of the content to different visitors and compare which variant is more engaging. To show the right variant to the right visitor: - Every visitor gets an ID that resolves to an A or a B variant. - In your front end, you need to send this ID along with the query to retrieve the right variant. - This is done by setting the value for the API request header, `Prepr-Customer-Id`, when you make the API request to retrieve the content. By [installing the Prepr Toolkit](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-3-set-up-data-collection#install-the-prepr-toolkit) in the previous section, you've already prepared your API request headers. ### Set the API request headers Since the API request headers are already prepared, you can simply set the API request headers for your page. To set the API request headers, update your `page.tsx` with the highlighted code below: ```tsx filename="./src/app/[[...slug]]/page.tsx" copy {6, 17-20} async function getData(slug: string) { const {data} = await getClient().query({ query: GetPageBySlugDocument, variables: { slug: slug, }, context: { // Call the getPreprHeaders function to get the appropriate headers headers: await getPreprHeaders() }, fetchPolicy: 'no-cache', }) if (!data.Page) { return notFound() } return data } { let { slug} = await params // Set the slug to the home page value if there's no slug if (!slug) { slug = '/' } // Add a forward slash to the slug to navigate to the correct page. if (slug instanceof Array) { slug = slug.join('/') } const data = await getData(slug) const elements = data.Page?.content.map((element, index) => { if (element.__typename === 'Feature') { return } else if (element.__typename === 'Hero') { return } }) return (
{elements}
); } ``` > **Note:** > > **Using Cache Components?** If you've enabled `cacheComponents` in Next.js 16, remove the `revalidate` and `dynamic` exports from the code above and wrap the page content in a `` boundary. > Check out the [caching strategies doc](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/caching-strategies#cache-components) for more details. ### Test your A/B test The A/B test is now running in your website. Let's check the traffic distribution for the A/B test. In your Prepr environment, go to the *Electric Lease Landing Page* content item and click the icon in your A/B test group and choose the **Manage settings** option. You'll notice the traffic distribution is set to 50%. This means that about 50% of visitors that visit the electric lease landing page will see variant A and about 50% will see variant B. ![change traffic distribution](https://assets-site.prepr.io/jchgr7zmuvi//electric-lease-ab-test-settings-traffic-distribution.png) To test that the A/B test in the electric lease landing page is showing variant A and variant B to different visitors, you can simulate this in your browser. Follow the steps below to do this simple test. 1. Go to `http://localhost:3000/electric-lease` to open the *Electric Lease Landing Page* in the browser. Take note of the heading in the top section. The text should read either *Drive Electric...* (Variant A) or *Go Electric...* (Variant B). 2. Right click the page to **Inspect the page**, click the **Application** tab. Under the cookies, click your localhost. You'll notice the `__prepr_uid` cookie in the list. 3. Right-click the localhost entry to clear the cookies and refresh the page again. If you still see the same variant, repeat the process. You might see the same variant a couple of times in a row. ![clear cookies](https://assets-site.prepr.io/3f26vhcduiy3//clear-cookies.png) When you see the other variant, you know that the A/B test is running successfully. > **Note:** > > There is an easier way to test your A/B test. We'll explore it in the last chapter of the Next.js complete guide when you [install the *Prepr preview toolbar*](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-6-install-the-preview-bar). ### Track impressions and conversion events Your A/B test is running successfully, however there are no metrics being recorded yet. You can see this with the **Awaiting data** message at the top of the A/B test in the *Electric Lease Landing Page* content item. ![A/B test without metrics](https://assets-site.prepr.io/2m79a6xol3dr//electric-lease-landing-page-awaiting-metrics-data.png) To get Prepr to start showing some metrics data, you need to add HTML attributes to elements in your A/B test. When you add these attributes, the tracking pixel automatically tracks impressions and your chosen conversion events. 1. To start collecting impressions and clicks, add the HTML attributes `data-prepr-variant-key` and `data-prepr-variant-event` to the *Hero* section as shown in the highlighted code below. The impression is recorded when the *Hero* element scrolls into view. The click is recorded when the *Button* element is clicked. ```tsx filename="./src/components/hero-section.tsx" copy {11-12, 19-20} import Link from 'next/link' import { HeroFragment } from '@/gql/graphql' import Image from 'next/image' import Button from '@/components/button' export default function HeroSection({ item }: { item: HeroFragment }) { const image = item.image return ( /* Set the HTML attribute with the matching data-prepr-variant-key to collect impressions for the whole section */

{item.heading}

{item.sub_heading}

{/* Set the HTML attribute to collect click events on the link */}
Hero Image
) } ``` > **Note:** > > If you want to track a conversion other than a click event, e.g. a quote request, then set your `data-prepr-variant-event` to the custom event. > For example: `data-prepr-variant-event="QuoteRequest"`. Check out the [A/B testing doc for more details](https://docs.prepr.io/ab-testing/setting-up-ab-testing). 2. To start recording metrics, go to the electric lease landing page in your website, in the top section click the **Find your car** button. 3. Now when you view the A/B test in your *Electric Lease Landing Page* content item, you can click the **Metrics** link and see the number of impressions and clicks like in the image below. ![A/B test metrics](https://assets-site.prepr.io/18ff40fgug2w//ab-test-example-metrics.png) Congratulations! You have a running A/B test with metrics in your Next.js website. Now, you can [Add personalization](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-5-add-personalization) to your website. Source: https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-4-add-ab-testing --- # Add personalization to your Next.js website *Prepr CMS enables editors to create adaptive content directly in their content items to personalize elements of web pages. This makes setting up personalization really simple and effective in increasing engagement and conversions. This chapter of the Next.js complete guide shows you how to add personalization to your Next.js website.* At the end of this section, you'll see a personalized home page for visitors interested in electric cars. ![Personalized home page](https://assets-site.prepr.io/50el53oh29zt//personalized-home-page-electric-lease.png) The steps below make use of the Adaptive content in the *Homepage* content item from the Acme Lease demo data. ![Home page adaptive content](https://assets-site.prepr.io/4p2wncoonmx2//home-page-content-item-with-adaptive-content.png) *Home page content item with Adaptive content for the Hero and Feature sections.* This chapter continues from the previous section, [*Set up data collection*](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-3-set-up-data-collection). If you haven't yet enabled Prepr tracking in your Next.js project, follow the steps in this section first. Otherwise, let's get started. ## Set up personalization for the home page You can show different personalized versions of the content to different groups (*Segments*) of visitors. To show the right variant to the right visitor: - Every visitor is given an ID that resolves to a segment they belong to. - In your front end, you need to send this ID along with the query to retrieve the right variant. - This is done by setting the value for the API request header, `Prepr-Customer-Id`, when you make the API request to retrieve the content. By [installing the Prepr Toolkit](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-3-set-up-data-collection#install-the-prepr-toolkit) in the previous section, you've already prepared your API request headers. ### Add visitors to segments For this setup we'll make use of the *Electric Car Buyers* segment in the Acme lease demo data. Make sure that the segment in your Prepr environment includes the condition, *Visitors who* `did` `view` `specific items`, `Electric Lease Landing Page`. ![electric car buyers segment](https://assets-site.prepr.io/6fefsntyp7zr//electric-car-buyers-segment.png) If you need more details, checkout the [Segments docs](https://docs.prepr.io/personalization/managing-segments). You previously [enabled Prepr tracking](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-3-set-up-data-collection#enable-prepr-tracking) in your website. This means a visitor gets added to the *Electric Car Buyers* segment whenever they view the *Electric Lease Landing Page* content item. You can test this with the following steps: 1. Go to `http://localhost:3000/electric-lease` to open the electric lease landing page in your website. ![Electric Car landing page](https://assets-site.prepr.io/4fxqpaodooxg//acme-lease-electric-car-landing-page.png) 2. In your Prepr environment, go to the **Segments** page and click the **Electric Car Buyers** segment. You should see matching visitors in this segment like in the image below. ![Electric Car segment visitors](https://assets-site.prepr.io/6ar98qwvhmlo//electric-car-buyers-highlight-matching-visitors.png) To make sure that visitors in the *Electric Car Buyers* segment see the matching adaptive content when they visit the home page, you need to set the API request header, `Prepr-Customer-Id`, when you retrieve the page content. Based on this value, Prepr will check the segment that this visitor belongs to and will retrieve the matching personalized variant. Because you [installed the Prepr Toolkit](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-3-set-up-data-collection#install-the-prepr-toolkit) previously, your API request headers are already prepared. ### Set the API request headers > **Note:** > > If you followed the previous guide to [add A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-4-add-ab-testing#set-the-api-request-headers), then you've already set the API request headers and can skip this step and go straight to [Test your adaptive content](#test-your-adaptive-content). Since the API request headers are already prepared, you can simply set the API request headers for your page. To set the API request headers, update your `page.tsx` with the highlighted code below: ```tsx filename="./src/app/[[...slug]]/page.tsx" copy {6, 17-20} async function getData(slug: string) { const {data} = await getClient().query({ query: GetPageBySlugDocument, variables: { slug: slug, }, context: { // Call the getPreprHeaders function to get the appropriate headers headers: await getPreprHeaders() }, fetchPolicy: 'no-cache', }) if (!data.Page) { return notFound() } return data } { let { slug} = await params // Set the slug to the home page value if there's no slug if (!slug) { slug = '/' } // Add a forward slash to the slug to navigate to the correct page. if (slug instanceof Array) { slug = slug.join('/') } const data = await getData(slug) const elements = data.Page?.content.map((element, index) => { if (element.__typename === 'Feature') { return } else if (element.__typename === 'Hero') { return } }) return (
{elements}
); } ``` > **Note:** > > **Using Cache Components?** If you've enabled `cacheComponents` in Next.js 16, remove the `revalidate` and `dynamic` exports from the code above and wrap the page content in a `` boundary. > Check out the [caching strategies doc](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/caching-strategies#cache-components) for more details. ### Test your adaptive content Now that people who visit the Electric lease landing page are being grouped into the *Electric Car Buyers* segment, you can test the adaptive content. To test the adaptive content for the *Electric Car Buyers* segment follow the steps below. 1. Go to the *Electric Lease Landing Page* in your website. For example: `http://localhost:3000/electric-lease`. 2. Wait a few seconds and go back to the home page. You can now see the personalized home page like in the image below. ![Personalized home page](https://assets-site.prepr.io/50el53oh29zt//personalized-home-page-electric-lease.png) To see the non-personalized page, you can reset the cookies and refresh the page as follows: 1. Right-click the page to **Inspect the page**, click the **Application** tab. Under the cookies, click your localhost. You'll notice the `__prepr_uid` cookie in the list. 2. Right-click the localhost entry to **Clear** the cookies and refresh the page. You should now see the non-personalized content in the home page. ![clear cookies](https://assets-site.prepr.io/3naav2tjzw12//clear-cookies-for-personalization.png) ### Track impressions and conversion events Your personalization is running successfully, however there are no metrics being recorded yet. You can see this with the **Awaiting data** message at the top of the Adaptive content block in the *Homepage* content item. ![Home page adaptive content no metrics](https://assets-site.prepr.io/5tw48qkr5w3h//home-page-content-item-with-adaptive-content-awaiting-data.png) To get Prepr to start showing some metrics data, you need to add HTML attributes to elements in your adaptive content. When you add these attributes, the tracking pixel you added in the previous section automatically tracks impressions and your chosen conversion events. 1. To start collecting impressions and clicks, add the HTML attributes `data-prepr-variant-key` and `data-prepr-variant-event` to the *Hero* section as shown in the highlighted code below. The impression is recorded when the *Hero* element scrolls into view. The click is recorded when the *Button* element is clicked. > **Note:** > > If you followed the previous guide to [add A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-4-add-ab-testing#track-impressions-and-conversion-events), then you've already added the necessary HTML attributes and can skip this step. ```tsx filename="./src/components/hero-section.tsx" copy {11-12, 19-20} import Link from 'next/link' import { HeroFragment } from '@/gql/graphql' import Image from 'next/image' import Button from '@/components/button' export default function HeroSection({ item }: { item: HeroFragment }) { const image = item.image return ( /* Set the HTML attribute with the matching data-prepr-variant-key to collect impressions for the whole section */

{item.heading}

{item.sub_heading}

{/* Set the HTML attribute to collect click events on the link */}
Hero Image
) } ``` > **Note:** > > If you want to track a conversion other than a click event, e.g. a quote request, then set your `data-prepr-variant-event` to the custom event. > For example: `data-prepr-variant-event="QuoteRequest"`. Check out the [Personalization doc for more details](https://docs.prepr.io/personalization/setting-up-personalization#track-impressions-and-conversions). 2. To start recording metrics, go to the home page in your website, click the **Find your car** button. 3. Now when you view the adaptive content in your *Homepage* content item, you can click the **Metrics** link and see the number of impressions and clicks like in the image below. ![Adaptive content metrics](https://assets-site.prepr.io/6j0q5cpmiu7e//home-page-adaptive-content-metrics-example.png) Congratulations! You have successfully set up personalization with metrics in your Next.js website. Now, you can [install the preview bar](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-6-install-the-preview-bar) to your website. Source: https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-5-add-personalization --- # Install the *Prepr preview toolbar* *This guide shows you how to install the Prepr preview toolbar to easily review A/B testing and Adaptive content directly in your Next.js website.* At the end of this section, you can use the Prepr preview toolbar in your website. ![Prepr preview toolbar](https://assets-site.prepr.io/545864k940pz//prepr-preview-toolbar.png) ## Add the *Prepr preview toolbar* to your website The steps below assume that you already have [A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-4-add-ab-testing) or [personalization](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-5-add-personalization) set up in your Next.js website. If you haven't yet added A/B testing or personalization, follow the steps in these sections first. Otherwise, let's get started. > **Note:** > > When you installed the [Prepr Toolkit](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-3-set-up-data-collection#install-the-prepr-toolkit) previously, it included the *Prepr preview toolbar* which is pre-configured for adaptive content and A/B testing. ### Enable the *Prepr preview toolbar* The *Prepr preview toolbar* is already installed, so the next step is to enable it. To enable the preview toolbar in your Next.js front end, follow the steps below. 1. Add the `PREPR_ENV` variable to the `.env` file. You can enable the preview toolbar for a staging environment by setting the value to `preview`. > **Note:** > > Set this variable to `production` when you deploy your website to production so that the preview toolbar component doesn't get displayed in your live website. ```bash copy filename="./.env" {2} PREPR_GRAPHQL_URL= PREPR_ENV=preview ``` 2. Since we are setting up the configuration for the staging environment, update the value of the `PREPR_GRAPHQL_URL` variable to the *API URL* of the *GraphQL Preview* access token. This access token allows content editors to preview unpublished content items. ![preview access token](https://assets-site.prepr.io/56ce41l9ppm7//graphql-preview-access-token.png) 3. The preview toolbar component fetches all segments from the Prepr API. So, you need to give it access to do this by ticking the **Enable edit mode** checkbox in the access token. Click the **Save** button. > **Note:** > > **Invisible unicode characters** > > When using the preview access token, you'll notice your query responses include additional unicode characters for the whitespace in the text fields. > When you turn on edit mode in an access token, Stega-encoding serializes metadata into invisible UTF-8 encoded characters and appends them to string values. ![example query response with unicode characters](https://assets-site.prepr.io/23a2ny9qei6r//stega-encoding-example.png) 4. So far, your web app is setting the headers based on the visitor cookies to determine how to render the A/B testing and adaptive content. To make sure the Prepr preview toolbar overrides the headers to switch between the A/B testing and adaptive content variants, update the `proxy.ts` to enable the preview option. ```ts filename="./src/proxy.ts" copy {5} import type { NextRequest } from 'next/server' import { createPreprMiddleware } from '@preprio/toolkit/nextjs' export function proxy(request: NextRequest) { return createPreprMiddleware(request, { preview: true }) } ``` ### Add the preview toolbar component Now that the preview access token is updated, you can render the preview toolbar in your website, by creating a new `layout.tsx` file in the catch-all folder `[[...slug]]` with the following code. ```ts filename="./src/app/[[...slug]]/layout.tsx" copy // Helper function to get all the props for the PreprToolbar component (this needs a server component) // Get the props for the PreprToolbar component and check that the environment variable is set to preview const isPreview = process.env.PREPR_ENV === 'preview' let toolbarProps = null // Wrap in try-catch to handle cases where headers() can't be called during static generation if (isPreview) { try { toolbarProps = await getToolbarProps(process.env.PREPR_GRAPHQL_URL!) } catch (error) { // During static generation (e.g., for not-found pages), headers() may not be available // Silently fail and render without the toolbar console.error('Failed to fetch toolbar props:', error) toolbarProps = null } } return ( <> {isPreview && toolbarProps ? ( <> {children} ) : ( children )} ) } ``` > **Note:** > > **Using Cache Components?** If you've enabled `cacheComponents` in Next.js 16, move the `getToolbarProps` call to a separate component inside the `` boundary. > To make sure content editors always see the latest preview content, also enable *Draft Mode*. > Check out the [caching strategies doc](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/caching-strategies#preview-content-with-draft-mode) for more details. When you open your website in the browser, you can see the Prepr icon on the page. ### Test the preview toolbar Now that the preview toolbar is being rendered, you can test it with the following steps: 1. Stop the localhost website server (`CTRL-C`) if it's running and restart it with the `npm run dev` terminal command. 2. Go to the home page of your website, open the Prepr preview toolbar and click the **Reset** button. 3. Choose the **Electric Car Lovers** segment. You'll see the personalized header for this segment. ![preview toolbar - electric car segment](https://assets-site.prepr.io/po31rlv9lap//toolbar-electric-car-segment.png) 3. Go to `http://localhost:3000/electric-lease` to open the *Electric Lease Landing Page* in the browser and choose the B variant. You'll see that the text changes to the B variant content. ![preview toolbar - variant b](https://assets-site.prepr.io/3sqcdajzao8d//toolbar-b-variant-example.png) ## All done Congratulations! You have successfully installed the Prepr preview toolbar in your Next.js website. This brings you to the end of the Next.js complete guide which has given you all the tools and tips you need to create your own web app connected to Prepr CMS complete with personalization, A/B testing and a preview bar. Don't hesitate to [give us feedback](mailto:feedback@prepr.io) on your experience using this guide. ## Next steps To learn more on how to expand your project, check out the following resources: - [Content modeling examples](https://docs.prepr.io/content-modeling/examples) - [More data collection details](https://docs.prepr.io/data-collection) - [More about A/B testing](https://docs.prepr.io/ab-testing) - [More about personalization](https://docs.prepr.io/personalization) - [Deploy your Next app with Vercel](https://nextjs.org/learn-pages-router/basics/deploying-nextjs-app) Source: https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-6-install-the-preview-bar --- # Complete guide to Next.js and Prepr *This guide shows you how to connect Prepr to a Next.js project including styling, adaptive content, A/B testing and a preview bar.* Follow the steps below in the recommended order to set up your own working Next.js website with adaptive content, A/B testing and a preview bar. You can also customize this project to fit the requirements for your web app. - [Introduction](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/introduction): Get a preview of the website you'll build and what you need to get started. - [Set up a Next.js project](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-1-set-up-a-nextjs-project): Install and create a working Next.js project with static webpages. - [Make it dynamic](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-2-make-the-project-dynamic): Connect to Prepr using Apollo Client to retrieve and show Acme Lease content. - [Set up data collection](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-3-set-up-data-collection): Set up data collection for A/B testing and personalization. - [Add A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-4-add-ab-testing): Set up and see how an A/B test runs in your website. - [Add personalization](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-5-add-personalization): Deliver a personalized experience to the visitor. - [Install the preview toolbar](https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide/step-6-install-the-preview-bar): Make use of the Prepr Toolkit to preview content more easily for A/B testing and personalization. Source: https://docs.prepr.io/connecting-a-front-end-framework/nextjs/next-complete-guide --- # Introduction to the Astro complete guide *This section introduces you to the complete guide that shows you how to connect Prepr to an Astro project including styling, adaptive content, and A/B testing and a preview toolbar.* At the end of the complete guide, you’ll have a working website with personalization and A/B testing like the images below. You can customize this project to fit the requirements for your web app. **Home page for general website visitors** \ ![Dynamic home page](https://assets-site.prepr.io/2mka8wqtorsh//home-page-example-no-toolbar.png) **Two variants of a running A/B test on a landing page**\ ![Electric landing page with A/B test](https://assets-site.prepr.io/2u8iwxsr0nls//a-b-test-on-home-page.png) **Personalized home page for electric car lovers**\ ![Personalized home page](https://assets-site.prepr.io/20ew6450salq//personalized-home-page-example-no-toolbar.png) > **Note:** > > If you can't wait and want to skip ahead, clone the [repository on GitHub](https://github.com/preprio/astro-complete-starter) to run the demo locally or visit our [demo website](https://acme-lease.prepr.io/) to see it in action. > These resources and this guide are based on the latest version of Astro. ## Prerequisites Make sure the following is in place before getting started: - [A free Prepr account](https://signup.prepr.io/?plan=free) - [An environment with Acme Lease demo data in Prepr](https://docs.prepr.io/project-setup/setting-up-environments#create-an-environment) - [The latest version of Node.js](https://nodejs.org/en/download) Now that you know what to expect and have your Prepr environment ready, you can get started with the first step to [set up the Astro project](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-1-set-up-astro-project). Source: https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/introduction --- # Set up an Astro project *The instructions below will guide you through the first step of the Astro complete guide. These show you how to create an Astro website with static components.* ![Astro site with static components](https://assets-site.prepr.io/20xbdto8zyy2//astro-static-home-page.png) *Astro website with static text and images.* ## Create an Astro website with static components If you have an existing Astro project then you can skip this section and continue with the next section to [make your Astro project dynamic with Prepr content](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-2-make-the-project-dynamic). Otherwise, let's get started. ### Install an Astro project 1. Open a terminal, go to your development projects folder and execute the command below to create a new Astro project called `astro-complete-starter`. ```bash copy npm create astro@latest astro-complete-starter ``` Choose the `Use minimal (empty) template` option and use default options for the remaining prompts. 2. When the project is successfully created, go to the `astro-complete-starter` folder, the root directory of the project, and run the project with the following commands in the terminal: ```bash copy cd astro-complete-starter ``` ```bash copy npm run dev ``` 3. You should now be able to view your app on your localhost, for example, `http://localhost:4321/`. ### Set up styling with Tailwind Follow the steps below to add server-side rendering and some simple styling to the project. 1. In the terminal, stop the server you started in the above step (`CTRL-C`) and while in your project `astro-complete-starter` directory, run the Astro CLI to add the Node adapter (needed to server-render every request later in this guide) and Tailwind CSS: ```bash copy npx astro add node tailwind ``` Accept the prompts to install the packages and update your config. 2. Open the Astro project with your preferred code editor. 3. The `astro add tailwind` command scaffolds `src/styles/global.css`. Rename the css file to `globals.css` and add colors and spacing highlighted below: ```css filename="./src/styles/globals.css" {3-25} copy @import "tailwindcss"; @theme { --text-mb-4xl: 2rem; --text-mb-5xl: 2.675rem; --spacing-8xl: 90rem; --container-8xl: 90rem; --container-prose: 50rem; --color-gray-50: #EEEEEE; --color-primary-50: #EFF6FF; --color-primary-100: #DBEAFE; --color-primary-600: #2563EB; --color-primary-700: #1D4ED8; --color-secondary-500: #64748B; --color-secondary-700: #334155; } /* Horizontal page gutter for section containers, matching the Acme Lease demo site. */ @utility p-spacing { @apply px-4 sm:px-6 lg:px-8 xl:px-18 2xl:px-20; } ``` ### Add a simple navigation bar 1. Create a `components` folder in the `src` directory and add the following three components to your Astro project to display a navigation bar at the top of your website. **Button** ```astro filename="./src/components/Button.astro" copy --- import type { HTMLAttributes } from 'astro/types'; interface Props extends HTMLAttributes<'button'> { buttonStyle?: 'primary' | 'secondary'; } const { buttonStyle = 'primary', class: className, ...attrs } = Astro.props; --- ``` **Logo** ```astro filename="./src/components/Logo.astro" copy --- interface Props { text?: boolean; } const { text = true } = Astro.props; --- {text ? ( ) : ( )} ``` **Navigation bar** ```astro filename="./src/components/NavBar.astro" copy --- import Logo from './Logo.astro'; import Button from './Button.astro'; const navItems = [ { text: 'Find a car', href: '#' }, { text: 'About Acme', href: '#' }, { text: 'Blog', href: '#' }, { text: 'Contacts', href: '#' }, ]; ---
``` Before you can view the navigation bar, you need to include it in the project layout. ### Update the project layout Follow the steps below to update the project layout to include the navigation bar and use Tailwind classes for styling. 1. In `src` create a new folder, `layouts` and add the code below to a new `Layout.astro` file, if it doesn't already exist. ```astro filename="./src/layouts/Layout.astro" copy --- import { Font } from 'astro:assets'; import NavBar from '../components/NavBar.astro'; import '../styles/globals.css'; --- Prepr Astro complete starter ``` > **Note:** > > The `` component and `cssVariable` come from the `fonts` entry you'll add to `astro.config.mjs` in the next step. > Astro resolves and self-hosts the Ubuntu font instead of loading it from Google at request time. 2. Update `astro.config.mjs` to declare the Ubuntu font: ```mjs filename="./astro.config.mjs" copy {2, 7-9, 11-19} // @ts-check import { defineConfig, fontProviders } from 'astro/config'; import node from '@astrojs/node'; import tailwindcss from '@tailwindcss/vite'; export default defineConfig({ // Render every request on the server: personalization and A/B testing // depend on per-visitor request headers. output: 'server', adapter: node({ mode: 'standalone' }), fonts: [ { provider: fontProviders.google(), name: 'Ubuntu', cssVariable: '--font-ubuntu', weights: [400, 700], subsets: ['latin'], }, ], vite: { plugins: [tailwindcss()], }, }); ``` In the `src/pages` folder, replace the code in the `index.astro` file with the following code to display your page. ```astro filename="./src/pages/index.astro" copy --- ---

My home page

``` 3. Restart the localhost server with `npm run dev` in the terminal and go back to your browser. You should see the navigation bar and your heading text like in the image below. ![Update page with navigation bar](https://assets-site.prepr.io/4q5lqmjxqit//my-first-home-page-astro-localhost.png) Once your Astro project is created and working, you can create a static page with components. ### Create static components In this step, you'll create a static version of the page including two front-end components. One for a *Hero* section at the top of the page and another for *Feature* sections to highlight features for the company. Add these components to the front end as follows: 1. Go to the `src/pages` folder and rename `index.astro` to `[...slug].astro`. This new file name creates a [catch-all route](https://docs.astro.build/en/guides/routing/#example-dynamic-pages-at-multiple-levels) to all your pages. 2. Refresh your page in the browser to check if your website still works. 3. Replace the code in `[...slug].astro` with the code below to display the components with static data. ```astro filename="./src/pages/[...slug].astro" copy --- import Layout from '../layouts/Layout.astro'; import Hero from '../components/sections/Hero.astro'; import Feature from '../components/sections/Feature.astro'; --- ``` > **Note:** > > You'll notice compilation errors in this page because the components don't exist yet. Continue the next steps to create these components and resolve these errors. 4. In the `src/components` folder, create a new folder called `sections`. In this new folder, create new files called `Hero.astro` and `Feature.astro` and copy the code below to construct these components. **Hero section** ```astro filename="./src/components/sections/Hero.astro" copy --- import Button from '../Button.astro'; ---

Acme Lease

A car leasing company

``` **Feature section** ```astro filename="./src/components/sections/Feature.astro" copy --- import Button from '../Button.astro'; interface Props { align?: 'left' | 'right'; } const { align = 'left' } = Astro.props; ---

Feature heading

A short text that explains the feature.

``` 5. Restart the localhost server with `npm run dev` in the terminal and go back to your browser. You should now see something like the image below. ![Astro site with static components](https://assets-site.prepr.io/20xbdto8zyy2//astro-static-home-page.png) Great work on getting your static web app working! Continue your journey to the next section to [make your Astro project dynamic with Prepr content](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-2-make-the-project-dynamic). Source: https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-1-set-up-astro-project --- # Make your Astro project dynamic *This guide shows you how to connect an existing Astro project to Prepr to retrieve and show Acme Lease demo content.* ## Connect your Astro website to Prepr The steps below continue from the previous section, [Set up an Astro project](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-1-set-up-astro-project). If you don't yet have an Astro website with static pages, follow the steps in this section first. Otherwise, let's get started. ### Connect to Prepr To connect directly to Prepr CMS, add a `fetch` helper that sends GraphQL queries to the Prepr API. 1. Stop the localhost website server (`CTRL-C`) if it's running and execute the following command in the terminal to install GraphQL packages: ```bash copy npm add @graphql-typed-document-node/core graphql ``` 2. Create a `lib` folder in the `src` directory. Then create a file called `prepr.ts` in this folder. Copy the following code to this file to set up the fetch helper: ```ts filename="./src/lib/prepr.ts" copy import { PREPR_GRAPHQL_URL } from 'astro:env/server'; import { print } from 'graphql'; import type { TypedDocumentNode } from '@graphql-typed-document-node/core'; export async function prepr( document: TypedDocumentNode, variables: TVariables, headers: Record = {}, ): Promise { const response = await fetch(PREPR_GRAPHQL_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', ...headers }, body: JSON.stringify({ query: print(document), variables }), }); if (!response.ok) { throw new Error(`Prepr GraphQL request failed: ${response.status} ${response.statusText}`); } const { data, errors } = await response.json(); if (errors?.length) throw new Error(errors[0].message); return data; } ``` 3. We recommend using environment variables to store sensitive information like access tokens. To add environment variables, create a `.env` file in the root directory of your project and add the access token like this: ```bash filename="./.env" copy PREPR_GRAPHQL_URL= ``` 4. Replace the placeholder value `` with the *API URL* of an access token from Prepr. Get an access token by logging into your Prepr account:\ a. Click the icon and choose the **Access tokens** option to view all the access tokens.\ b. Click to open the *GraphQL Production* access token, and copy the *API URL* to only retrieve published content items on your site for now. c. Paste the copied *API URL* in your `.env` file. > **Note:** > > Use the *GraphQL Production* access token to request published content items for your live app and use the *GraphQL Preview* token to make a [preview of unpublished content items](https://docs.prepr.io/project-setup/setting-up-previews-and-visual-editing) for your content editors. ![GraphQL production access token example](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) 5. Declare the `PREPR_GRAPHQL_URL` variable in your `astro.config.mjs` so Astro validates and types it. At this stage, also add the `image.domains` entry to fetch page images from the Prepr demo environment. ```mjs filename="./astro.config.mjs" copy {2, 22-31} // @ts-check import { defineConfig, envField, fontProviders } from 'astro/config'; import node from '@astrojs/node'; import tailwindcss from '@tailwindcss/vite'; export default defineConfig({ // Render every request on the server: personalization and A/B testing // depend on per-visitor request headers. output: 'server', adapter: node({ mode: 'standalone' }), fonts: [ { provider: fontProviders.google(), name: 'Ubuntu', cssVariable: '--font-ubuntu', weights: [400, 700], subsets: ['latin'], }, ], vite: { plugins: [tailwindcss()], }, image: { // Domain of the Acme Lease demo images. Add your own asset domain here. domains: ['demo-patterns.stream.prepr.io'], }, env: { schema: { PREPR_GRAPHQL_URL: envField.string({ context: 'server', access: 'secret' }), }, }, }); ``` 6. Execute the following command to restart the server and refresh your page in the browser. ```bash copy npm run dev ``` If your website runs without errors, then the setup above was done correctly. Now that Astro can reach the Prepr API, you can retrieve content from Prepr. Before we create a query to retrieve content, let's follow some best practices for TypeScript and install a GraphQL code generator. ### Install a code generator In this step, you'll install a GraphQL code generator to generate TypeScript types automatically for your queries and keep these up to date with any schema changes. Check out the [TypeScript best practices doc](https://docs.prepr.io/development/best-practices/typescript#install-a-graphql-code-generator) for more details. In the meantime, follow the steps below to install the GraphQL code generator. 1. Stop the localhost website server (`CTRL-C`) if it's running and execute the following commands in your terminal: ```bash copy npm add -D @graphql-codegen/cli @graphql-codegen/client-preset @graphql-codegen/introspection npm add -D dotenv npm add -D @astrojs/check typescript ``` > **Note:** > > When you run the `build` script, it runs `astro check`, which needs `@astrojs/check` and `typescript` installed 2. Once done, create a new file, `codegen.ts` in the root of your project. Update it with the code below. ```ts filename="./codegen.ts" copy import 'dotenv/config'; import type { CodegenConfig } from '@graphql-codegen/cli'; const config: CodegenConfig = { overwrite: true, schema: process.env.PREPR_GRAPHQL_URL, documents: ['src/queries/**/*.graphql'], generates: { 'src/gql/': { preset: 'client', presetConfig: { fragmentMasking: false }, // Astro's strict tsconfig enables verbatimModuleSyntax. config: { useTypeImports: true }, }, './graphql.schema.json': { plugins: ['introspection'] }, }, }; export default config; ``` 3. To complete the codegen installation, update the `scripts` block in your `package.json` so `build` runs codegen and a type check before `astro build`, and add a `codegen` script. Leave the rest of your `package.json` (name, version, dependencies) as-is — only the `scripts` block changes: ```json filename="./package.json" copy {5-6,9} { ... "scripts": { "dev": "astro dev", "build": "npm run codegen && astro check && astro build", "start": "node --env-file-if-exists=.env ./dist/server/entry.mjs", "preview": "astro preview", "astro": "astro", "codegen": "graphql-codegen --config codegen.ts" } ... } ``` > **Note:** > > The built server doesn't load `.env` on its own. > `--env-file-if-exists=.env` tells Node to load it, so `PREPR_GRAPHQL_URL` and other secrets are available when you run `npm start` after `npm run build`. > > Host-provided environment variables, for example, in a deployment platform, still take precedence over `.env`. 4. Execute the following command to restart the server and refresh your page in the browser to make sure the fetch helper is set up correctly: ```bash copy npm run dev ``` If your website runs without errors, then the setup above was done correctly. Now you can add a query to your project. ### Add a GraphQL query Once the GraphQL code generator is installed, you can add a query to get the content for your home page from Prepr. If you’re using preloaded demo data in your Prepr environment as mentioned in the [Prerequisites](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/introduction#prerequisites), you should have a *Homepage* content item like in the images below. The *Homepage* has all the elements of the page in a [*Stack* field](https://docs.prepr.io/content-modeling/field-types#stack-field). The stack makes it easy for an editor to set up their page content in a flexible structure for the front end. ![Home page content item - stack field](https://assets-site.prepr.io/3e0ain6d3kyj//home-page-content-item-highlight-stack-field.png) > **Note:** > > You can see that the hero and feature sections are in adaptive content blocks. We'll explain that in more detail in the upcoming personalization chapter. Follow the steps below to add a query to retrieve the id, title, slug, and a stack containing the hero and feature sections with their own fields: 1. Create a `queries` folder in the `src` directory of your project and create a file named `get-page-by-slug.graphql`. 2. Add the following queries in the `queries` folder to retrieve a page by its slug: > **Note:** > > You'll notice that we've created separate fragment queries for the button, hero and feature UI components in a `fragments` folder. > Fragments in GraphQL are a way to define a set of fields that can be reused. **Page query** ```graphql filename="./src/queries/get-page-by-slug.graphql" copy # The GetPageBySlug query uses a slug value from the input parameter to retrieve a specific page query GetPageBySlug($slug: String) { Page(slug: $slug) { title _id content { __typename ... on Hero { ...Hero } ... on Feature { ...Feature } } } } ``` **Button fragment query** ```graphql filename="./src/queries/fragments/button.fragment.graphql" copy fragment Button on Button { button_type text external_url link { ... on Category { _slug } ... on Page { _slug } ... on Post { _slug } } } ``` **Feature fragment query** ```graphql filename="./src/queries/fragments/feature.fragment.graphql" copy fragment Feature on Feature { _id heading sub_heading button { ...Button } _context { variant_key } image_position image { url(width: 870, height: 570) } } ``` **Hero fragment query** ```graphql filename="./src/queries/fragments/hero.fragment.graphql" copy fragment Hero on Hero { _id sub_heading image { url(preset: "Hero", width: 2000) height width } _context { variant_key } heading buttons { ...Button } } ``` > **Note:** > > You can create and [test GraphQL queries](https://docs.prepr.io/graphql-api/test-queries) using the [Apollo explorer](https://studio.apollographql.com/sandbox/explorer) from Prepr. > Open the **API Explorer** from the *Homepage* content item or from your access token page. ### Generate TypeScript types 1. Now that the query is created, stop the localhost website server (`CTRL-C`) and generate the TypeScript types by running the following command in the terminal: ```bash copy npm run codegen ``` 2. If the command is successful you'll see new files in the `src/gql` folder, including `graphql.ts`. Check out the [TypeScript doc](https://docs.prepr.io/development/best-practices/typescript#generate-typescript-types) for more details. 3. Test the query by updating the `[...slug].astro` file. The code below executes the query and returns early with a 404 rewrite if the page doesn't exist. ```astro filename="./src/pages/[...slug].astro" copy {5-6, 8-13} --- import Layout from '../layouts/Layout.astro'; import Hero from '../components/sections/Hero.astro'; import Feature from '../components/sections/Feature.astro'; import { prepr } from '../lib/prepr'; import { GetPageBySlugDocument } from '../gql/graphql'; // The home page has no slug in the URL; in Prepr its slug is '/'. const slug = Astro.params.slug ?? '/'; const data = await prepr(GetPageBySlugDocument, { slug }); if (!data.Page) return Astro.rewrite('/404'); const page = data.Page; --- ``` 4. You need a page to rewrite to when the slug isn't found. Create a new page called `404.astro`: ```astro filename="./src/pages/404.astro" copy --- Astro.response.status = 404; ---

404

This page could not be found.

``` Now that the query has been created and retrieves the data successfully, fetch and show the specific page content from the query results. ### Fetch page content To view the Prepr content in your website, you need to fetch the page elements. As mentioned previously, the page has elements in a *Stack* field. You'll fetch two components, a *Hero* section and *Feature* sections in the stack. 1. Next, update your components to display data dynamically. **Hero section** ```astro filename="./src/components/sections/Hero.astro" copy {2-3, 6-11, 17-18, 21, 27} --- interface Props { item: HeroFragment; } const { item } = Astro.props; const image = item.image; ---

{item.heading}

{item.sub_heading}

{image?.url && Hero Image}
``` **Feature section** ```astro filename="./src/components/sections/Feature.astro" copy {2-3, 7, 10, 16-20, 22-24, 26-30} --- interface Props { item: FeatureFragment; } const { item } = Astro.props; ---
{item.image_position === 'LEFT' && item.image?.url && (
Feature image
)}

{item.heading}

{item.sub_heading}

{item.image_position === 'RIGHT' && item.image?.url && (
Feature image
)}
``` 2. Now update `[...slug].astro` to fetch and render the elements from the stack: ```astro filename="./src/pages/[...slug].astro" copy {17-20} --- // The home page has no slug in the URL; in Prepr its slug is '/'. const slug = Astro.params.slug ?? '/'; const data = await prepr(GetPageBySlugDocument, { slug }); if (!data.Page) return Astro.rewrite('/404'); const page = data.Page; --- {page.content.map((element) => { if (element.__typename === 'Hero') return ; if (element.__typename === 'Feature') return ; })} ``` > **Note:** > > In later chapters, this guide shows you how to implement Personalization and A/B testing which are dynamic features in Prepr. > These features work best with [SSR (Server-side rendering)](https://docs.prepr.io/development/best-practices/csr-ssr-ssg#server-side-rendering-ssr). > The `output: 'server'` setting in `astro.config.mjs` renders every request on the server, so there's no build-time cache to disable — Astro's SSR mode always fetches fresh data per request. Now when you view the website on your localhost, you'll see something like the image below: ![Dynamic home page](https://assets-site.prepr.io/2mka8wqtorsh//home-page-example-no-toolbar.png) > **Note:** > > **Troubleshooting**\ > > **Runtime error**: `500` response with `Error: ` in the terminal.\ > > **Cause**: The `PREPR_GRAPHQL_URL` value is missing or incorrect, so the GraphQL API returns an error.\ > > **Solution**: Check the [Connect to Prepr](#connect-to-prepr) section and confirm the *API URL* in your `.env` file matches an access token in your Prepr environment.\ > Congratulations! You have successfully connected your front end to Prepr to make your website dynamic. Continue your journey to the next section to [set up data collection](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-3-set-up-data-collection). Source: https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-2-make-the-project-dynamic --- # Set up data collection with tracking and the Prepr Toolkit *Prepr is a CMS that includes built-in A/B testing and personalization features. These capabilities require visitor data to measure your test results and to build segments for personalization. This chapter of the Astro complete guide shows you how to enable tracking to collect this data in your Astro front end and how to simplify this data collection with the Prepr Toolkit.* The steps below continue from the previous section, [Make your Astro project dynamic](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-2-make-the-project-dynamic). If you don't yet have an Astro project connected to Prepr, follow the previous steps listed in the [Complete guide overview](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide) to create one. Otherwise, let's get started. ## Install the Prepr Toolkit We developed the [Prepr Toolkit](https://github.com/preprio/prepr-toolkit) to simplify working with event data for A/B testing and personalization. In the steps below, you'll enable tracking in your website by adding the *Prepr Tracking Code* to your front end. This code generates the `__prepr_uid` cookie for each website visitor. This visitor gets stored in Prepr with this unique ID, if they don't already exist. This means you can use this `__prepr_uid` cookie value to set the API request header, `Prepr-Customer-Id`, when you retrieve the page content for A/B testing and personalization. The *Prepr Toolkit* prepares the `Prepr-Customer-Id` API request header for you, using the `__prepr_uid` cookie value. Based on this value, Prepr retrieves the right content as follows: - A/B testing: Prepr decides which variant, A or B, to return in the content. - Personalization: Prepr checks the segment that this visitor belongs to and retrieves the matching personalized variant. To install the *Prepr Tookit*, follow the steps below. 1. In your Astro project, stop the localhost website server (`CTRL-C`) if it's running and install the package with the following terminal command: ```bash copy npm install @preprio/toolkit ``` Once done, you need to call a function in the package to prepare the API request headers before the page gets rendered in your website. 2. To do this, go to your project and and create a new file `middleware.ts` in the `src` folder. Then add the following code to perform the package's preprocessing logic on user requests. ```ts filename="./src/middleware.ts" copy import { defineMiddleware } from 'astro:middleware'; import { onPreprRequest } from '@preprio/toolkit/astro'; export const onRequest = defineMiddleware((context, next) => onPreprRequest(context, next, { preview: false }), ); ``` ## Enable Prepr tracking and collect view events ### Enable Prepr tracking When you enable Prepr tracking in your front end, it captures visitor data and lets you track how they engage with your content. Follow the steps below to enable Prepr tracking: 1. Go to the `src/layouts` folder and add the highlighted code below to the `Layout.astro` file. ```astro filename="./src/layouts/Layout.astro" copy {4-6, 9, 21} --- import { Font } from 'astro:assets'; import NavBar from '../components/NavBar.astro'; import PreprTrackingPixel from '@preprio/toolkit/astro/components/PreprTrackingPixel'; import { extractAccessToken } from '@preprio/toolkit'; import { PREPR_GRAPHQL_URL } from 'astro:env/server'; import '../styles/globals.css'; const accessToken = extractAccessToken(PREPR_GRAPHQL_URL); --- Prepr Astro complete starter {accessToken && } ``` > **Note:** > > To see the tracking pixel value, in your Prepr environment, click the icon and choose the **Event management** option. > Click the **Get tracking code** link to view the *Prepr Tracking Code*. > > ![event tracking page](https://assets-site.prepr.io/2ovyjc1jscr8//updated-event-tracking-screenshot.png) ### Track page view events For the subsequent A/B testing and personalization sections in this guide, we want to track page view events. To do this, add a content item meta property to the `[...slug].astro` file like in the highlighted code below. The tracking pixel automatically recognizes this property and records the view event in Prepr. Check out the [data collection events docs](https://docs.prepr.io/data-collection/recording-events) for more details. ```astro filename="./src/pages/[...slug].astro" copy {17} --- // The home page has no slug in the URL; in Prepr its slug is '/'. const slug = Astro.params.slug ?? '/'; const data = await prepr(GetPageBySlugDocument, { slug }); if (!data.Page) return Astro.rewrite('/404'); const page = data.Page; --- {page.content.map((element) => { if (element.__typename === 'Hero') return ; if (element.__typename === 'Feature') return ; })} ``` For more details on how to track other types of events, check out the [data collection docs](https://docs.prepr.io/data-collection/recording-events). ### Test data collection You can easily check if the data collection is successful with the following steps. 1. Execute the following command to restart the server and refresh your page in the browser. ```bash copy npm run dev ``` 2. In your Prepr environment, go to the **Segments** page. 3. If the page view is recorded successfully you'll see a recent visitor in the **All visitors** list. 4. Click to open this visitor and you should see the *View* event on the *Homepage* similar to the image below. ![page view event](https://assets-site.prepr.io/3im9gynz7ubp//page-view-events.png) Congratulations! You've successfully enabled Prepr tracking in your Astro front end, started collecting page view events, and installed the Prepr Toolkit for A/B testing and personalization. Now, you're ready to [add A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-4-add-ab-testing) and [personalization](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-5-add-personalization) to your website. Source: https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-3-set-up-data-collection --- # Add A/B testing to your Astro website *Prepr CMS enables editors to create A/B tests directly within their content. This means testing different content versions is simple and effective. This chapter of the Astro complete guide demonstrates how to set up A/B testing in Prepr, including testing the display of variants and measuring the results.* At the end of this section, you'll see the A and B versions on the *Electric Lease Landing Page*. ![Electric landing page with A/B test](https://assets-site.prepr.io/2u8iwxsr0nls//a-b-test-on-home-page.png) The steps below make use of the A/B test in the *Electric Lease Landing Page* content item from the Acme Lease demo data. ![A/B test in Electric lease content item](https://assets-site.prepr.io/6ox5hri28pd6//electric-lease-landing-page.png) This chapter continues from the previous section, [Set up data collection](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-3-set-up-data-collection). If you haven't yet enabled Prepr tracking in your Astro project, follow the steps in this section first. Otherwise, let's get started. ## Set up A/B testing for the *Electric Lease Landing Page* You can run an A/B test on parts of a web page to show two different versions of the content to different visitors and compare which variant is more engaging. To show the right variant to the right visitor: - Every visitor gets an ID that resolves to an A or a B variant. - In your front end, you need to send this ID along with the query to retrieve the right variant. - This is done by setting the value for the API request header, `Prepr-Customer-Id`, when you make the API request to retrieve the content. By [installing the Prepr Toolkit](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-3-set-up-data-collection#install-the-prepr-toolkit) in the previous section, you've already prepared your API request headers. ### Set the API request headers Now that the Prepr Toolkit is installed, update the helper `prepr.ts` you created previously by replacing the headers type with the `PreprHeaders` type from the toolkit. ```ts filename="./src/lib/prepr.ts" copy {4, 9} document: TypedDocumentNode, variables: TVariables, headers: PreprHeaders = {}, ): Promise { const response = await fetch(PREPR_GRAPHQL_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', ...headers }, body: JSON.stringify({ query: print(document), variables }), }); if (!response.ok) { throw new Error(`Prepr GraphQL request failed: ${response.status} ${response.statusText}`); } const { data, errors } = await response.json(); if (errors?.length) throw new Error(errors[0].message); return data; } ``` Since the API request headers are already prepared, you can simply set them for your page. To set the API request headers, update the `[...slug].astro` with the highlighted code below: ```astro filename="./src/pages/[...slug].astro" copy {7, 12-13} --- // The home page has no slug in the URL; in Prepr its slug is '/'. const slug = Astro.params.slug ?? '/'; // Prepr uses these headers (visitor ID, segments, A/B variant) to pick the right variant. const data = await prepr(GetPageBySlugDocument, { slug }, getPreprHeaders(Astro.request.headers)); if (!data.Page) return Astro.rewrite('/404'); const page = data.Page; --- {page.content.map((element) => { if (element.__typename === 'Hero') return ; if (element.__typename === 'Feature') return ; })} ``` ### Test your A/B test The A/B test is now running in your website. Let's check the traffic distribution for the A/B test. In your Prepr environment, go to the *Electric Lease Landing Page* content item and click the icon in your A/B test group and choose the **Manage settings** option. You'll notice the traffic distribution is set to 50%. This means that about 50% of visitors that visit the electric lease landing page will see variant A and about 50% will see variant B. ![change traffic distribution](https://assets-site.prepr.io/jchgr7zmuvi//electric-lease-ab-test-settings-traffic-distribution.png) To test that the A/B test in the electric lease landing page is showing variant A and variant B to different visitors, you can simulate this in your browser. Follow the steps below to do this simple test. 1. Go to `http://localhost:4321/electric-lease` to open the *Electric Lease Landing Page* in the browser. Take note of the heading in the top section. The text should read either *Drive Electric...* (Variant A) or *Go Electric...* (Variant B). 2. Right click the page to **Inspect the page**, click the **Application** tab. Under the cookies, click your localhost. You'll notice the `__prepr_uid` cookie in the list. 3. Right-click the localhost entry to clear the cookies and refresh the page again. If you still see the same variant, repeat the process. You might see the same variant a couple of times in a row. ![clear cookies](https://assets-site.prepr.io/3r1e73tw32cf//electric-lease-inspect-page-to-clear-cookies.png) When you see the other variant, you know that the A/B test is running successfully. > **Note:** > > There is an easier way to test your A/B test. We'll explore it in the last chapter of the Astro complete guide when you [install the *Prepr preview toolbar*](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-6-install-the-preview-bar). ### Track impressions and conversion events Your A/B test is running successfully, however there are no metrics being recorded yet. You can see this with the **Awaiting data** message at the top of the A/B test in the *Electric Lease Landing Page* content item. ![A/B test without metrics](https://assets-site.prepr.io/2m79a6xol3dr//electric-lease-landing-page-awaiting-metrics-data.png) To get Prepr to start showing some metrics data, you need to add HTML attributes to elements in your A/B test. When you add these attributes, the tracking pixel automatically tracks impressions and your chosen conversion events. 1. To start collecting impressions and clicks, add the HTML attributes `data-prepr-variant-key` and `data-prepr-variant-event` to the *Hero* section as shown in the highlighted code below. The impression is recorded when the *Hero* element scrolls into view. The click is recorded when the *Button* element is clicked. ```astro filename="./src/components/sections/Hero.astro" copy {14-15, 22-23} --- import { Image } from 'astro:assets'; import type { HeroFragment } from '../../gql/graphql'; import Button from '../Button.astro'; interface Props { item: HeroFragment; } const { item } = Astro.props; const image = item.image; --- {/* data-prepr-variant-key on the section records an impression for the variant */}

{item.heading}

{item.sub_heading}

{/* data-prepr-variant-event on the link records a click (conversion) */}
{image?.url && Hero Image}
``` > **Note:** > > If you want to track a conversion other than a click event, e.g. a quote request, then set your `data-prepr-variant-event` to the custom event. > For example: `data-prepr-variant-event="QuoteRequest"`. Check out the [A/B testing doc for more details](https://docs.prepr.io/ab-testing/setting-up-ab-testing). 2. To start recording metrics, go to the electric lease landing page in your website, in the top section click the **Find your car** button. 3. Now when you view the A/B test in your *Electric Lease Landing Page* content item, you can click the **Metrics** link and see the number of impressions and clicks like in the image below. ![A/B test metrics](https://assets-site.prepr.io/18ff40fgug2w//ab-test-example-metrics.png) Congratulations! You have a running A/B test with metrics in your Astro website. Now, you can [Add personalization](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-5-add-personalization) to your website. Source: https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-4-add-ab-testing --- # Add personalization to your Astro website *Prepr CMS enables editors to create adaptive content directly in their content items to personalize elements of web pages. This makes setting up personalization really simple and effective in increasing engagement and conversions. This chapter of the Astro complete guide shows you how to add personalization to your Astro website.* At the end of this section, you'll see a personalized home page for visitors interested in electric cars. ![Personalized home page](https://assets-site.prepr.io/20ew6450salq//personalized-home-page-example-no-toolbar.png) The steps below make use of the Adaptive content in the *Homepage* content item from the Acme Lease demo data. ![Home page with adaptive content](https://assets-site.prepr.io/4p2wncoonmx2//home-page-content-item-with-adaptive-content.png) *Home page content item with Adaptive content for the Hero and Feature sections.* This chapter continues from the previous section, [*Set up data collection*](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-3-set-up-data-collection). If you haven't yet enabled Prepr tracking in your Astro project, follow the steps in this section first. Otherwise, let's get started. ## Set up personalization for the home page You can show different personalized versions of the content to different groups (*Segments*) of visitors. To show the right variant to the right visitor: - Every visitor is given an ID that resolves to a segment they belong to. - In your front end, you need to send this ID along with the query to retrieve the right variant. - This is done by setting the value for the API request header, `Prepr-Customer-Id`, when you make the API request to retrieve the content. By [installing the Prepr Toolkit](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-3-set-up-data-collection#install-the-prepr-toolkit) in the previous section, you've already prepared your API request headers. ### Add visitors to segments For this setup we'll make use of the *Electric Car Buyers* segment in the Acme lease demo data. Make sure that the segment in your Prepr environment includes the condition, *Visitors who* `did` `view` `specific items`, `Electric Lease Landing Page`. ![electric car buyers segment](https://assets-site.prepr.io/6fefsntyp7zr//electric-car-buyers-segment.png) If you need more details, checkout the [Segments docs](https://docs.prepr.io/personalization/managing-segments). You previously [enabled Prepr tracking](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-3-set-up-data-collection#enable-prepr-tracking) in your website. This means a visitor gets added to the *Electric Car Buyers* segment whenever they view the *Electric Lease Landing Page* content item. You can test this with the following steps: 1. Go to `http://localhost:4321/electric-lease` to open the electric lease landing page in your website. ![Electric Car landing page](https://assets-site.prepr.io/5gdtnolfj93z//astro-electric-lease-landing-page.png) 2. In your Prepr environment, go to the **Segments** page and click the **Electric Car Buyers** segment. You should see matching visitors in this segment like in the image below. ![Electric Car segment visitors](https://assets-site.prepr.io/6ar98qwvhmlo//electric-car-buyers-highlight-matching-visitors.png) To make sure that visitors in the *Electric Car Buyers* segment see the matching adaptive content when they visit the home page, you need to set the API request header, `Prepr-Customer-Id`, when you retrieve the page content. Based on this value, Prepr will check the segment that this visitor belongs to and will retrieve the matching personalized variant. Because you [installed the Prepr Toolkit](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-3-set-up-data-collection#install-the-prepr-toolkit) previously, your API request headers are already prepared. ### Set the API request headers > **Note:** > > If you followed the previous guide to [add A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-4-add-ab-testing#set-the-api-request-headers), then you've already set the API request headers and can skip this step and go straight to [Test your adaptive content](#test-your-adaptive-content). Now that the Prepr Toolkit is installed, update the helper `prepr.ts` you created previously by replacing the headers type with the `PreprHeaders` type from the toolkit. ```ts filename="./src/lib/prepr.ts" copy {4, 9} document: TypedDocumentNode, variables: TVariables, headers: PreprHeaders = {}, ): Promise { const response = await fetch(PREPR_GRAPHQL_URL, { method: 'POST', headers: { 'Content-Type': 'application/json', ...headers }, body: JSON.stringify({ query: print(document), variables }), }); if (!response.ok) { throw new Error(`Prepr GraphQL request failed: ${response.status} ${response.statusText}`); } const { data, errors } = await response.json(); if (errors?.length) throw new Error(errors[0].message); return data; } ``` Since the API request headers are already prepared, you can simply set them for your page. To set the API request headers, update the `[...slug].astro` with the highlighted code below: ```astro filename="./src/pages/[...slug].astro" copy {7, 12-13} --- // The home page has no slug in the URL; in Prepr its slug is '/'. const slug = Astro.params.slug ?? '/'; // Prepr uses these headers (visitor ID, segments, A/B variant) to pick the right variant. const data = await prepr(GetPageBySlugDocument, { slug }, getPreprHeaders(Astro.request.headers)); if (!data.Page) return Astro.rewrite('/404'); const page = data.Page; --- {page.content.map((element) => { if (element.__typename === 'Hero') return ; if (element.__typename === 'Feature') return ; })} ``` ### Test your adaptive content Now that people who visit the Electric lease landing page are being grouped into the *Electric Car Buyers* segment, you can test the adaptive content. To test the adaptive content for the *Electric Car Buyers* segment follow the steps below. 1. Go to the *Electric Lease Landing Page* in your website. For example: `http://localhost:4321/electric-lease`. 2. Then, go back to the home page. You can now see the personalized home page like in the image below. ![Personalized home page](https://assets-site.prepr.io/20ew6450salq//personalized-home-page-example-no-toolbar.png) To see the non-personalized page, you can reset the cookies and refresh the page as follows: 1. Right-click the page to **Inspect the page**, click the **Application** tab. Under the cookies, click your localhost. You'll notice the `__prepr_uid` cookie in the list. 2. Right-click the localhost entry to **Clear** the cookies and refresh the page. You should now see the non-personalized content in the home page. ![clear cookies](https://assets-site.prepr.io/6ven1tbaq6lq//home-page-inspect-clear-cookies.png) ### Track impressions and conversion events Your personalization is running successfully, however there are no metrics being recorded yet. You can see this with the **Awaiting data** message at the top of the Adaptive content block in the *Homepage* content item. ![Home page adaptive content no metrics](https://assets-site.prepr.io/5tw48qkr5w3h//home-page-content-item-with-adaptive-content-awaiting-data.png) To get Prepr to start showing some metrics data, you need to add HTML attributes to elements in your adaptive content. When you add these attributes, the tracking pixel you added in the previous section automatically tracks impressions and your chosen conversion events. 1. To start collecting impressions and clicks, add the HTML attributes `data-prepr-variant-key` and `data-prepr-variant-event` to the *Hero* section as shown in the highlighted code below. The impression is recorded when the *Hero* element scrolls into view. The click is recorded when the *Button* element is clicked. > **Note:** > > If you followed the previous guide to [add A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-4-add-ab-testing#track-impressions-and-conversion-events), then you've already added the necessary HTML attributes and can skip this step. ```astro filename="./src/components/sections/Hero.astro" copy {14-15, 22-23} --- import { Image } from 'astro:assets'; import type { HeroFragment } from '../../gql/graphql'; import Button from '../Button.astro'; interface Props { item: HeroFragment; } const { item } = Astro.props; const image = item.image; --- {/* data-prepr-variant-key on the section records an impression for the variant */}

{item.heading}

{item.sub_heading}

{/* data-prepr-variant-event on the link records a click (conversion) */}
{image?.url && Hero Image}
``` > **Note:** > > If you want to track a conversion other than a click event, e.g. a quote request, then set your `data-prepr-variant-event` to the custom event. > For example: `data-prepr-variant-event="QuoteRequest"`. Check out the [Personalization doc for more details](https://docs.prepr.io/personalization/setting-up-personalization#track-impressions-and-conversions). 2. To start recording metrics, go to the home page in your website, click the **Find your car** button. 3. Now when you view the adaptive content in your *Homepage* content item, you can click the **Metrics** link and see the number of impressions and clicks like in the image below. ![Adaptive content metrics](https://assets-site.prepr.io/6j0q5cpmiu7e//home-page-adaptive-content-metrics-example.png) Congratulations! You have successfully set up personalization with metrics in your Astro website. Now, you can [install the preview toolbar](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-6-install-the-preview-bar) to your website. Source: https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-5-add-personalization --- # Install the *Prepr preview toolbar* *This guide shows you how to install the Prepr preview toolbar to easily review A/B testing and Adaptive content directly in your Astro website.* At the end of this section, you can use the Prepr preview toolbar in your website. ![Prepr preview toolbar](https://assets-site.prepr.io/545864k940pz//prepr-preview-toolbar.png) ## Add the *Prepr preview toolbar* to your website The steps below assume that you already have [A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-4-add-ab-testing) or [personalization](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-5-add-personalization) set up in your Astro website. If you haven't yet added A/B testing or personalization, follow the steps in these sections first. Otherwise, let's get started. > **Note:** > > When you installed the [Prepr Toolkit](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-3-set-up-data-collection#install-the-prepr-toolkit) previously, it included the *Prepr preview toolbar* which is pre-configured for adaptive content and A/B testing. ### Enable the *Prepr preview toolbar* The *Prepr preview toolbar* is already installed, so the next step is to enable it. To enable the preview toolbar in your Astro front end, follow the steps below. 1. Add the `PREPR_ENV` variable to the `.env` file. You can enable the preview toolbar for a staging environment by setting the value to `preview`. > **Note:** > > Set this variable to `production` when you deploy your website to production so that the preview toolbar component doesn't get displayed in your live website. ```bash copy filename="./.env" {2} PREPR_GRAPHQL_URL= PREPR_ENV=preview ``` 2. Since we are setting up the configuration for the staging environment, update the value of the `PREPR_GRAPHQL_URL` variable to the *API URL* of the *GraphQL Preview* access token. This access token allows content editors to preview unpublished content items. ![preview access token](https://assets-site.prepr.io/56ce41l9ppm7//graphql-preview-access-token.png) 3. The preview toolbar component fetches all segments from the Prepr API. So, you need to give it access to do this by ticking the **Enable edit mode** checkbox in the access token. Click the **Save** button. > **Note:** > > **Invisible unicode characters** > > When using the preview access token, you'll notice your query responses include additional unicode characters for the whitespace in the text fields. > When you turn on edit mode in an access token, Stega-encoding serializes metadata into invisible UTF-8 encoded characters and appends them to string values. ![example query response with unicode characters](https://assets-site.prepr.io/23a2ny9qei6r//stega-encoding-example.png) 4. Add the `PREPR_ENV` variable to the environment schema in `astro.config.mjs` so it's typed and validated like `PREPR_GRAPHQL_URL`: ```mjs filename="./astro.config.mjs" copy {30-35} // @ts-check import { defineConfig, envField, fontProviders } from 'astro/config'; import node from '@astrojs/node'; import tailwindcss from '@tailwindcss/vite'; export default defineConfig({ // Render every request on the server: personalization and A/B testing // depend on per-visitor request headers. output: 'server', adapter: node({ mode: 'standalone' }), fonts: [ { provider: fontProviders.google(), name: 'Ubuntu', cssVariable: '--font-ubuntu', weights: [400, 700], subsets: ['latin'], }, ], vite: { plugins: [tailwindcss()], }, image: { // Domain of the Acme Lease demo images. Add your own asset domain here. domains: ['demo-patterns.stream.prepr.io'], }, env: { schema: { PREPR_GRAPHQL_URL: envField.string({ context: 'server', access: 'secret' }), PREPR_ENV: envField.enum({ context: 'server', access: 'secret', values: ['preview', 'production'], default: 'production', }), }, }, }); ``` 5. So far, your web app is setting the headers based on the visitor cookies to determine how to render the A/B testing and adaptive content. To make sure the Prepr preview toolbar overrides the headers to switch between the A/B testing and adaptive content variants, update `middleware.ts` to enable the preview option. ```ts filename="./src/middleware.ts" copy {2,6-7} import { defineMiddleware } from 'astro:middleware'; import { PREPR_ENV } from 'astro:env/server'; import { onPreprRequest } from '@preprio/toolkit/astro'; export const onRequest = defineMiddleware((context, next) => // In preview, the toolbar can override the visitor's segment and A/B variant. onPreprRequest(context, next, { preview: PREPR_ENV === 'preview' }), ); ``` ### Add the preview toolbar component Now that the preview access token is updated, you can render the preview toolbar in your website, by adding it to `Layout.astro` with the following code. ```astro filename="./src/layouts/Layout.astro" copy {5, 7-8, 12-13, 31} --- const accessToken = extractAccessToken(PREPR_GRAPHQL_URL); const toolbarProps = PREPR_ENV === 'preview' ? await getToolbarProps(Astro.request.headers, PREPR_GRAPHQL_URL) : null; --- Prepr Astro complete starter {accessToken && } {toolbarProps && } ``` When you open your website in the browser, you can see the Prepr icon on the page. ### Test the preview toolbar Now that the preview toolbar is being rendered, you can test it with the following steps: 1. Stop the localhost website server (`CTRL-C`) if it's running and restart it with the `npm run dev` terminal command. 2. Go to the home page of your website, open the Prepr preview toolbar and click the **Reset** button. 3. Choose the **Electric Car Lovers** segment. You'll see the personalized header for this segment. ![preview toolbar - electric car segment](https://assets-site.prepr.io/po31rlv9lap//toolbar-electric-car-segment.png) 3. Go to `http://localhost:4321/electric-lease` to open the *Electric Lease Landing Page* in the browser and choose the B variant. You'll see that the text changes to the B variant content. ![preview toolbar - variant b](https://assets-site.prepr.io/3sqcdajzao8d//toolbar-b-variant-example.png) ## All done Congratulations! You have successfully installed the Prepr preview toolbar in your Astro website. This brings you to the end of the Astro complete guide which has given you all the tools and tips you need to create your own web app connected to Prepr CMS complete with personalization, A/B testing and a preview toolbar. Don't hesitate to [give us feedback](mailto:feedback@prepr.io) on your experience using this guide. ## Next steps To learn more on how to expand your project, check out the following resources: - [Content modeling examples](https://docs.prepr.io/content-modeling/examples) - [More data collection details](https://docs.prepr.io/data-collection) - [More about A/B testing](https://docs.prepr.io/ab-testing) - [More about personalization](https://docs.prepr.io/personalization) Source: https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-6-install-the-preview-bar --- # Complete guide to Astro and Prepr *This guide shows you how to connect Prepr to an Astro project including styling, adaptive content, A/B testing and a preview toolbar.* Follow the steps below in the recommended order to set up your own working Astro website with adaptive content, A/B testing and a preview toolbar. You can also customize this project to fit the requirements for your web app. - [Introduction](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/introduction): Get a preview of the website you'll build and what you need to get started. - [Set up an Astro project](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-1-set-up-astro-project): Install and create a working Astro project with static webpages. - [Make it dynamic](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-2-make-the-project-dynamic): Connect to Prepr to retrieve and show Acme Lease content. - [Set up data collection](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-3-set-up-data-collection): Set up data collection for A/B testing and personalization. - [Add A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-4-add-ab-testing): Set up and see how an A/B test runs in your website. - [Add personalization](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-5-add-personalization): Deliver a personalized experience to the visitor. - [Install the preview toolbar](https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide/step-6-install-the-preview-bar): Make use of the Prepr Toolkit to preview content more easily for A/B testing and personalization. Source: https://docs.prepr.io/connecting-a-front-end-framework/astro/astro-complete-guide --- # Introduction to the Nuxt complete guide *This section introduces you to the complete guide that shows you how to connect Prepr to a Nuxt project including styling, adaptive content, A/B testing and a preview bar.* At the end of the complete guide, you’ll have a working website with personalization and A/B testing like the images below. You can customize this project to fit the requirements for your web app. **Home page for general website visitors** \ ![Dynamic home page](https://assets-site.prepr.io/2mka8wqtorsh//home-page-example-no-toolbar.png) **Two variants of a running A/B test on a landing page**\ ![Electric landing page with A/B test](https://assets-site.prepr.io/2u8iwxsr0nls//a-b-test-on-home-page.png) **Personalized home page for electric car lovers**\ ![Personalized home page](https://assets-site.prepr.io/20ew6450salq//personalized-home-page-example-no-toolbar.png) > **Note:** > > If you can't wait and want to skip ahead, clone the [repository on GitHub](https://github.com/preprio/nuxt-complete-starter) to run the demo locally or visit our [demo website](https://acme-lease.prepr.io/) to see it in action. > These resources and this guide are based on Nuxt 4. ## Prerequisites Make sure the following is in place before getting started: - [A free Prepr account](https://signup.prepr.io/?plan=free) - [An environment with Acme Lease demo data in Prepr](https://docs.prepr.io/project-setup/setting-up-environments#create-an-environment) - [Node.js](https://nodejs.org/en/download) version 22.22 or later (Nuxt 4 requires `^22.22.3`, `^24.15.0` or `>=26`) Now that you know what to expect and have your Prepr environment ready, you can get started with the first step to [set up the Nuxt project](https://docs.prepr.io/connecting-a-front-end-framework/nuxtjs/nuxt-complete-guide/step-1-set-up-a-nuxt-project). Source: https://docs.prepr.io/connecting-a-front-end-framework/nuxtjs/nuxt-complete-guide/introduction --- # Set up a Nuxt project *The instructions below will guide you through the first step of the Nuxt complete guide. These show you how to create a Nuxt website with static components.* ![Nuxt site with static components](https://assets-site.prepr.io/2hkb8jfxzj9y//nuxt-website-with-static-components.png) *Nuxt website with static text and images.* ## Create a Nuxt website with static components Let's get started. ### Install a Nuxt project 1. Open a terminal, go to your development projects folder and execute the command below to create a new Nuxt project called `nuxt-complete-starter` from the minimal template. ```bash copy npm create nuxt@latest nuxt-complete-starter -- --template minimal ``` Choose `npm` as the package manager and skip the official modules for now. You'll add the module you need in the next section. 2. When the project is successfully created, go to the `nuxt-complete-starter` folder, the root directory of the project, and run the project with the following commands in the terminal: ```bash copy cd nuxt-complete-starter ``` ```bash copy npm run dev ``` 3. You should now be able to view your app on your localhost, for example, `http://localhost:3000/`. 4. Open the Nuxt project with your preferred code editor. > **Note:** > > Nuxt 4 keeps your application code in the `app` folder. > All the files you create in this guide go in `app`, except for the configuration files in the project root and the server middleware in `server`, which you'll add in step 3. 5. Update the `app.vue` file with the following code to display your home page: ```vue filename="./app/app.vue" copy ``` You should now see something like the image below on your localhost. ![Empty Nuxt site](https://assets-site.prepr.io/77n9yewiwz8l//nuxt-home-page.png) ### Install Tailwind Follow the steps below to install and set up Tailwind styling. 1. In the terminal, stop the server you started in the above step (`CTRL-C`), and install Tailwind to make styling easier in the components you'll create below. ```bash copy npm install tailwindcss @tailwindcss/vite ``` 2. Add the [Nuxt Fonts](https://fonts.nuxt.com/) module. It downloads the fonts you use in your CSS and serves them from your own website. ```bash copy npx nuxt module add fonts ``` 3. Create an `./app/assets/css/main.css` file to import the Tailwind CSS and to set up some additional colors and spacing: ```css filename="./app/assets/css/main.css" copy @import "tailwindcss"; @theme { --font-sans: "Ubuntu", sans-serif; --text-mb-4xl: 2rem; --text-mb-5xl: 2.675rem; --spacing-8xl: 90rem; --container-8xl: 90rem; --container-prose: 50rem; --color-gray-50: #EEEEEE; --color-primary-50: #EFF6FF; --color-primary-100: #DBEAFE; --color-primary-600: #2563EB; --color-primary-700: #1D4ED8; --color-secondary-500: #64748B; --color-secondary-700: #334155; } /* Horizontal page gutter for section containers, matching the Acme Lease demo site. */ @utility p-spacing { @apply px-4 sm:px-6 lg:px-8 xl:px-18 2xl:px-20; } ``` 4. Add the `@tailwindcss/vite` plugin and your new `main.css` to your Nuxt configuration. ```ts filename="nuxt.config.ts" copy {1,7-15} import tailwindcss from '@tailwindcss/vite' // https://nuxt.com/docs/api/configuration/nuxt-config export default defineNuxtConfig({ modules: ['@nuxt/fonts'], compatibilityDate: '2025-07-15', devtools: { enabled: true }, css: ['~/assets/css/main.css'], // @nuxt/fonts finds the Ubuntu font in main.css and self-hosts it. fonts: { defaults: { weights: [400, 700] }, }, vite: { plugins: [tailwindcss()], }, }) ``` 5. Test that Tailwind is correctly installed by updating the `app.vue` file as follows: ```vue filename="./app/app.vue" copy ``` 6. Restart the localhost with `npm run dev`. You should see updating styling like in the image below: ![Nuxt empty page - updated styling](https://assets-site.prepr.io/1bkvbi6aoap2//nuxt-home-page-with-tailwind-styling.png) ### Add a simple navigation bar 1. Create new `pages` folder in the `app` directory. Add a new file, called `[...slug].vue` to this subfolder. This file name creates a [catch-all route](https://nuxt.com/docs/4.x/directory-structure/app/pages#catch-all-route) to all your pages, including the home page. Copy and paste the following basic code to this new file. ```vue filename="./app/pages/[...slug].vue" copy ``` 2. Update the code in the `app.vue` to define a layout for the whole website. ```vue filename="./app/app.vue" copy ``` > **Note:** > > `` renders the page that matches the URL, in this case `[...slug].vue`. 3. Create a `components` folder in the `app` directory and add the following three components to your project to display a navigation bar at the top of your website. **Button** ```vue filename="./app/components/Button.vue" copy ``` **Logo** ```vue filename="./app/components/Logo.vue" copy ``` **Navigation bar** ```vue filename="./app/components/NavBar.vue" copy ``` > **Note:** > > Nuxt [auto-imports](https://nuxt.com/docs/guide/directory-structure/app/components) every component in the `components` folder, so `NavBar.vue` can use `` and ` ``` **Logo** ```html filename="./resources/views/components/logo.blade.php" copy ``` **Navigation bar** ```html filename="./resources/views/components/navbar.blade.php" copy @php $navItems = [ ['text' => 'Find a car', 'href' => '#'], ['text' => 'About Acme', 'href' => '/about-acme'], ['text' => 'Blog', 'href' => '#'], ['text' => 'Contacts', 'href' => '#'], ]; @endphp
``` 2. To view the navigation bar, include it in the page as follows: ```html filename="./resources/views/index.blade.php" copy {4} @vite('resources/css/app.css')

My home page

``` 3. Restart the localhost server with `npm run dev` in the terminal and go back to your browser. You should see the navigation bar and your heading text like in the image below. ![Update page with navigation bar](https://assets-site.prepr.io/1a3yr011ds66//laravel-empty-home-page-with-navigation-bar.png) Once your navigation bar is created and displays correctly, you can create a static page with components. ### Create static components In this step, you'll create a static version of the page to contain two front-end components. One for a *Hero* section at the top of the page and another for *Feature* sections to highlight features for the company. 1. Update the `index.blade.php` file to include the *Hero* and *Feature* components. ```html filename="./resources/views/pages/index.blade.php" copy {8-37} @vite('resources/css/app.css')
``` 2. Go to `resources/views/components` folder with new files called `hero-section.blade.php` and `feature-section.blade.php` and copy the code below to construct these components. **Hero section** ```html filename="./resources/views/components/hero-section.blade.php" copy

{{ data_get($data,'heading') }}

{{ data_get($data,'sub_heading') }}

Hero Image
``` **Feature section** ```html filename="./resources/views/components/feature-section.php" copy
Feature image

{{ data_get($data,'heading') }}

{{ data_get($data,'sub_heading') }}

{{ data_get($data,'button.text') }}
``` Now that the components are built, refresh the page. You should now see something like the image below on your localhost. ![Laravel Static home page](https://assets-site.prepr.io/5akwi5xsqx21//acme-lease-static-home-page.png) Great work on getting your static web app working! Continue your journey to the next section to [make your Laravel project dynamic with Prepr content](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-2-make-the-project-dynamic). Source: https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-1-set-up-a-laravel-project --- # Make your Laravel project dynamic *This guide shows you how to connect an existing Laravel project to Prepr to retrieve and show Acme Lease demo content.* ## Connect your Laravel website to Prepr The steps below continue from the previous section, [Set up a Laravel project](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-1-set-up-a-laravel-project). If you don't yet have a Laravel website with static pages, follow the steps in this section first. Otherwise, let's get started. ### Install the Laravel GraphQL SDK The [Laravel GraphQL SDK](https://github.com/preprio/laravel-graphql-sdk) is an integration tool that helps to retrieve Prepr CMS data with GraphQL. The instructions below show you how to install the Laravel GraphQL SDK so that you can execute GraphQL queries to request data from the Prepr API. 1. Execute the following command in the terminal: ```bash copy composer require preprio/laravel-graphql-sdk ``` 2. Once done, update the `services.php` file in the `config` folder to add the Prepr endpoint service: ```php filename="./config/services.php" copy {25-35} [ 'token' => env('POSTMARK_TOKEN'), ], 'ses' => [ 'key' => env('AWS_ACCESS_KEY_ID'), 'secret' => env('AWS_SECRET_ACCESS_KEY'), 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), ], 'resend' => [ 'key' => env('RESEND_KEY'), ], 'slack' => [ 'notifications' => [ 'bot_user_oauth_token' => env('SLACK_BOT_USER_OAUTH_TOKEN'), 'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'), ], ], /* Include the Prepr endpoint service */ 'prepr' => [ 'endpoint' => env('PREPR_ENDPOINT'), /* Optional timeout settings */ 'timeout' => env('PREPR_TIMEOUT'), 'connect_timeout' => env('PREPR_CONNECT_TIMEOUT') ], ]; ``` 3. Update the .env file in the root directory of your project by adding the Prepr endpoint below. ```bash filename="./.env" copy PREPR_ENDPOINT= # Optional variables for custom timeout values, Default Laravel defaults are 30 and 10 PREPR_TIMEOUT= PREPR_CONNECT_TIMEOUT= ``` 4. Replace the placeholder value `` with the *API URL* of an access token from Prepr. Get an access token by logging into your Prepr account:\ a. Click the icon and choose the **Access tokens** to view all the access tokens.\ b. Click to open the *GraphQL Production* access token, and copy the *API URL* to only retrieve published content items on your site for now. c. Paste the copied *API URL* in your `.env` file. > **Note:** > > Use the *GraphQL Production* access token to request published content items for your live app and use the *GraphQL Preview* token to make a [preview of unpublished content items](https://docs.prepr.io/project-setup/setting-up-previews-and-visual-editing) for your content editors. ![GraphQL production access token example](https://assets-site.prepr.io/nl31q4rdm7c//graphql-api-token-example.png) Now refresh the app in your browser, if your app runs without errors, then the setup above was done correctly. The next step is to fetch content from Prepr using the installed GraphQL SDK. ### Add a GraphQL query Now that the Laravel GraphQL SDK is installed and connected to Prepr, let's fetch the page content from Prepr. If you’re using preloaded demo data in your Prepr environment as mentioned in the [Prerequisites](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/introduction#prerequisites), you should have a *Homepage* content item like in the images below. The *Homepage* has all the elements of the page in a [*Stack* field](https://docs.prepr.io/content-modeling/field-types#stack-field). The stack makes it easy for an editor to set up their page content in a flexible structure for the front end. ![Home page content item - stack field](https://assets-site.prepr.io/3e0ain6d3kyj//home-page-content-item-highlight-stack-field.png) > **Note:** > > You can see that the hero and feature sections are in adaptive content blocks. We'll explain that in more detail in the upcoming personalization chapter. Follow the steps below to add a query to retrieve the id, title, slug, and a stack containing the hero and feature sections with their own fields: 1. In the `app` folder, create a `Queries` subfolder. In this folder, create a file named `get-page-by-slug.graphql` and add the following query to this file to retrieve a page by its slug: ```graphql filename="./app/Queries/get-page-by-slug.graphql" copy query GetPageBySlug($slug: String) { Page(slug: $slug) { title _id content { __typename ... on Hero { _id sub_heading image { url(preset: "Hero", width: 2000) height width } _context { variant_key } heading buttons { button_type text external_url link { ... on Category { _slug } ... on Page { _slug } ... on Post { _slug } } } } ... on Feature { _id heading sub_heading button { button_type text external_url link { ... on Category { _slug } ... on Page { _slug } ... on Post { _slug } } } _context { variant_key } image_position image { url(width: 870, height: 570) } } } } } ``` > **Note:** > > You can create and [test GraphQL queries](https://docs.prepr.io/graphql-api/test-queries) using the [Apollo explorer](https://studio.apollographql.com/sandbox/explorer) from Prepr. Open the **API Explorer** from the *Article* content item or the access token that you copied previously. 2. Test the query by going to the `app/Http/controllers` folder and adding the following code to the `PageController.php` file. This code executes the query with a slug value and prints the query results to the front end. ```php filename="./app/Http/controllers/PageController.php" copy {5-6,10-11, 13-31} 'get-page-by-slug', 'variables' => [ 'slug' => $slug, ], ]); $page = data_get($response->json(), 'data.Page'); // Print the query response to the browser dump($page); // Return the page view with query response if ($page) { return view('pages.index', [ 'page' => $page, ]); } } } ``` > **Note:** > > You can create and [test GraphQL queries](https://docs.prepr.io/graphql-api/test-queries) using the [Apollo explorer](https://studio.apollographql.com/sandbox/explorer) from Prepr. > Open the **API Explorer** from the *Homepage* content item or from your access token page. If you’re using preloaded demo data in your Prepr environment as mentioned above in the [Prerequisites](#prerequisites) section, you should have one home page with the slug `home`. The query will retrieve the id, title, slug, and a stack containing a page header and two image and text blocks with their own fields. When you refresh the page on the browser, the response to the query looks something like this: ![query results in browser](https://assets-site.prepr.io/6tjeq5e96vb4//laravel-console-query-results.png) ### Fetch page content Now that the query has been created and retrieves the data successfully, let's display the specific page content from the query results. 1. Go to the `resources/views/pages` and update the `index.blade.php` to display the data retrieved from the query instead of the static data. ```html filename="./resources/views/pages/index.blade.php" copy {7-18} @vite('resources/css/app.css')
@if(data_get($page,'content')) @foreach(data_get($page,'content') as $element) @if(data_get($element,'__typename') === 'Hero') @elseif(data_get($element,'__typename') === 'Feature') @endif @endforeach @endif
``` Now when you refresh the page, you'll see something like the image below: ![Laravel Dynamic home page](https://assets-site.prepr.io/4nsjg3xg5w9w//laravel-dynamic-site.png) Congratulations! You have successfully connected your front end to Prepr to make your website dynamic. Continue your journey to the next section to [set up data collection](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-3-set-up-data-collection). Source: https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-2-make-the-project-dynamic --- # Set up data collection with tracking *Prepr is a CMS that includes built-in A/B testing and personalization features. These capabilities require visitor data to measure your test results and to build segments for personalization. This chapter of the Laravel complete guide shows you how to enable tracking to collect this data in your Laravel front end.* ## Enable Prepr tracking and collect view events The steps below continue from the previous section, [Make your Laravel project dynamic](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-2-make-the-project-dynamic). If you don't yet have a Laravel project connected to Prepr, follow the previous steps listed in the [Complete guide overview](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide) to create one. Otherwise, let's get started. ### Enable Prepr tracking When you enable Prepr tracking in your front end, it captures visitor data and lets you track how they engage with your content. 1. In your Prepr environment, click the icon and choose the **Event management** option. 2. Click the **Get tracking code** link and copy the *Prepr Tracking Code*. ![event tracking page](https://assets-site.prepr.io/2ovyjc1jscr8//updated-event-tracking-screenshot.png) 3. In the `resources/views/pages` folder, update the `index.blade.php` including the *Prepr Tracking Code* you just copied. ```html filename="./resources/views/pages/index.blade.php" copy {5} @vite('resources/css/app.css')
@if(data_get($page,'content')) @foreach(data_get($page,'content') as $element) @if(data_get($element,'__typename') === 'Hero') @elseif(data_get($element,'__typename') === 'Feature') @endif @endforeach @endif
``` 4. Refresh your website page in the browser. If there are no errors in the terminal console, Prepr tracking is set up successfully. ### Track page view events For the subsequent A/B testing and personalization sections in this guide, we want to track page view events. To do this, add a content item meta property to the `index.blade.php` file like in the highlighted code below. The tracking pixel automatically recognizes this property and records the view event in Prepr. Check out the [data collection events docs](https://docs.prepr.io/data-collection/recording-events) for more details. ```html filename="./resources/views/pages/index.blade.php" copy {7} @vite('resources/css/app.css')
@if(data_get($page,'content')) @foreach(data_get($page,'content') as $element) @if(data_get($element,'__typename') === 'Hero') @elseif(data_get($element,'__typename') === 'Feature') @endif @endforeach @endif
``` For more details on how to track other types of events, check out the [data collection docs](https://docs.prepr.io/data-collection/recording-events). ### Test data collection You can easily check if the data collection is successful with the following steps. 1. Go to your website in the browser and refresh the page. 2. In your Prepr environment, go to the **Segments** page. 3. If the page view is recorded successfully you'll see a recent visitor in the **All visitors** list. 4. Click to open this visitor and you should see the *View* event on the *Homepage* similar to the image below. ![page view event](https://assets-site.prepr.io/3im9gynz7ubp//page-view-events.png) Congratulations! You've successfully enabled Prepr tracking in your Laravel front end, and started collecting page view events. Now, you're ready to [add A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-4-add-ab-testing) and [personalization](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-5-add-personalization) to your website. Source: https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-3-set-up-data-collection --- # Add A/B testing to your Laravel website *Prepr CMS enables editors to create A/B tests directly within their content. This means testing different content versions is simple and effective. This chapter of the Laravel complete guide demonstrates how to set up A/B testing in Prepr, including testing the display of variants and measuring the results.* At the end of this section, you'll see the A and B versions on the *Electric Lease Landing Page*. ![Electric landing page with A/B test](https://assets-site.prepr.io/2u8iwxsr0nls//a-b-test-on-home-page.png) The steps below make use of the A/B test in the *Electric Lease Landing Page* content item from the Acme Lease demo data. ![A/B test in Electric lease content item](https://assets-site.prepr.io/6ox5hri28pd6//electric-lease-landing-page.png) This chapter continues from the previous section, [Set up data collection](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-3-set-up-data-collection). If you haven't yet enabled Prepr tracking in your Laravel project, follow the steps in this section first. Otherwise, let's get started. ## Set up A/B testing for the *Electric Lease Landing Page* You can run an A/B test on parts of a web page to show two different versions of the content to different visitors and compare which variant is more engaging. To show the right variant to the right visitor: - Every visitor gets an ID that resolves to an A or a B variant. - In your front end, you need to send this ID along with the query to retrieve the right variant. - This is done by setting the value for the API request header, `Prepr-Customer-Id`, when you make the API request to retrieve the content. ### Set the API request headers When you enable [Prepr tracking](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-3-set-up-data-collection#add-the-tracking-code), it assigns each visitor a unique ID with a cookie called `__prepr_uid`. You can use the `__prepr_uid` value to set the *Prepr Customer ID*. > **Note:** > > The latest version of Laravel automatically tries to decrypt cookies. This results in the `__prepr_uid` value being set to null when the page is loaded. > To exclude this cookie from the decryption process, you first need to extend the system code by adding it an exception. 1. Go to the `bootstrap` folder and add the highlighted code below to the `app.php` file. ```php filename="./bootstrap/app.php" copy {14-16} withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { $middleware->encryptCookies(except: [ '__prepr_uid', ]); }) ->withExceptions(function (Exceptions $exceptions) { // }) ->create(); ``` 2. Go to the `app/Http/controllers` folder and add the following code to the `PageController.php` to get the `__prepr_uid` cookie value and use it to set the API request header `Prepr-Customer-Id`. ```php filename="./app/Http/controllers/PageController.php" copy {8-9, 13, 22 ,20-22} 'get-page-by-slug', 'variables' => [ 'slug' => $slug, ], 'headers' => [ 'Prepr-Customer-Id' => $request->cookie('__prepr_uid') ] ]); $page = data_get($response->json(), 'data.Page'); // Return the page view with query response if ($page) { return view('pages.index', [ 'page' => $page, ]); } } } ``` ### Test your A/B test The A/B test is now running in your website. Let's check the traffic distribution for the A/B test. In your Prepr environment, go to the *Electric Lease Landing Page* content item and click the icon in your A/B test group and choose the **Manage settings** option. You'll notice the traffic distribution is set to 50%. This means that about 50% of visitors who open the electric lease landing page see variant A and about 50% see variant B. ![change traffic distribution](https://assets-site.prepr.io/jchgr7zmuvi//electric-lease-ab-test-settings-traffic-distribution.png) To test that the A/B test in the electric lease landing page is showing variant A and variant B to different visitors, you can simulate this in the browser. Follow the steps below to do this simple test. 1. Go to `http://localhost:8000/electric-lease` to open the *Electric Lease Landing Page* in the browser. Take note of the heading in the top section. The text should read either *Drive Electric...* (Variant A) or *Go Electric...* (Variant B). 2. Right click the page to **Inspect the page**, click the **Application** tab. Under the cookies, click your localhost. You'll notice the `__prepr_uid` cookie in the list. 3. Right-click the localhost entry to clear the cookies and refresh the page again. If you still see the same variant, repeat the process. You might see the same variant a couple of times in a row. ![clear cookies](https://assets-site.prepr.io/3f26vhcduiy3//clear-cookies.png) When you see the other variant, you know that the A/B test is running successfully. ### Track impressions and conversion events Your A/B test is running successfully, however there are no metrics being recorded yet. You can see this with the **Awaiting data** message at the top of the A/B test in the *Electric Lease Landing Page* content item. ![A/B test without metrics](https://assets-site.prepr.io/3ma70q9z2wwf//acme-lease-ab-test-example-awaiting-data.png) To get Prepr to start showing some metrics data, you need to add HTML attributes to elements in your A/B test. When you add these attributes, the Prepr tracking pixel automatically tracks impressions and your chosen conversion events. 1. To start collecting impressions and clicks, add the HTML attributes `data-prepr-variant-key` and `data-prepr-variant-event` to the *Hero* section as shown in the highlighted code below. The impression is recorded when the *Hero* element scrolls into view. The click is recorded when the button is clicked. ```html filename="./resources/views/components/hero-section.blade.php" copy {1, 12}

{{ data_get($data,'heading') }}

{{ data_get($data,'sub_heading') }}

Hero Image
``` > **Note:** > > If you want to track a conversion other than a click event, e.g. a quote request, then set your `data-prepr-variant-event` to the custom event. > For example: `data-prepr-variant-event="QuoteRequest"`. Check out the [A/B testing doc for more details](https://docs.prepr.io/ab-testing/setting-up-ab-testing). 2. To start recording metrics, go to the electric lease landing page in your website, in the top section click the **Find your car** button. 3. Now when you view the A/B test in your *Electric Lease Landing Page* content item, you can click the **Metrics** link and see the number of impressions and clicks like in the image below. ![A/B test metrics](https://assets-site.prepr.io/18ff40fgug2w//ab-test-example-metrics.png) Congratulations! You have a running A/B test with metrics in your Laravel website. Now, you can [Add personalization](https://docs.prepr.io/connecting-a-front-end-framework/nuxtjs/nuxt-complete-guide/step-5-add-personalization) to your website. Source: https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-4-add-ab-testing --- # Add personalization to your Laravel website *Prepr CMS enables editors to create adaptive content directly in their content items to personalize elements of web pages. This makes setting up personalization really simple and effective in increasing engagement and conversions. This chapter of the Laravel complete guide shows you how to add personalization to your Laravel website.* At the end of this section, you'll see a personalized home page for visitors interested in electric cars. ![Personalized home page](https://assets-site.prepr.io/50el53oh29zt//personalized-home-page-electric-lease.png) The steps below make use of the Adaptive content in the *Homepage* content item from the Acme Lease demo data. ![Home page adaptive content](https://assets-site.prepr.io/4p2wncoonmx2//home-page-content-item-with-adaptive-content.png) *Home page content item with Adaptive content for the Hero and Feature sections.* This chapter continues from the previous section, [Set up data collection](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-3-set-up-data-collection). If you haven't yet enabled Prepr tracking in your Laravel project, follow the steps in this section first. Otherwise, let's get started. ## Set up personalization for the home page You can show different personalized versions of the content to different groups (*Segments*) of visitors. To show the right variant to the right visitor: - Every visitor is given an ID that resolves to a segment they belong to. - In your front end, you need to send this ID along with the query to retrieve the right variant. - This is done by setting the value for the API request header, `Prepr-Customer-Id`, when you make the API request to retrieve the content. ### Add visitors to segments For this setup we'll make use of the *Electric Car Buyers* segment in the Acme lease demo data. Make sure that the segment in your Prepr environment includes the condition, *Visitors who* `did` `view` `specific items`, `Electric Lease Landing Page`. ![electric car buyers segment](https://assets-site.prepr.io/6fefsntyp7zr//electric-car-buyers-segment.png) If you need more details, checkout the [Segments docs](https://docs.prepr.io/personalization/managing-segments). You previously [enabled Prepr tracking](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-3-set-up-data-collection#enable-prepr-tracking) in your website. This means a visitor gets added to the *Electric Car Buyers* segment whenever they view the *Electric Lease Landing Page* content item. You can test this with the following steps: 1. Go to `http://localhost:8000/electric-lease` to open the electric lease landing page in your website. ![Electric Car landing page](https://assets-site.prepr.io/3rnhzekktzsd//electric-lease-landing-page.png) 2. In your Prepr environment, go to the **Segments** page and click the **Electric Car Buyers** segment. You should see matching visitors in this segment like in the image below. ![Electric Car segment visitors](https://assets-site.prepr.io/6ar98qwvhmlo//electric-car-buyers-highlight-matching-visitors.png) To make sure that visitors in the *Electric Car Buyers* segment see the matching adaptive content when they visit the home page, you need to set the API request header, `Prepr-Customer-Id`, when you retrieve the page content. Based on this value, Prepr will check the segment that this visitor belongs to and will retrieve the matching personalized variant. When you enable [Prepr tracking](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-3-set-up-data-collection#add-the-tracking-code), it assigns each visitor a unique ID with a cookie called `__prepr_uid`. You can use the `__prepr_uid` value to set the *Prepr Customer ID*. > **Note:** > > The latest version of Laravel automatically tries to decrypt cookies. This results in the `__prepr_uid` value being set to null when the page is loaded. > To exclude this cookie from the decryption process, you first need to extend the system code by adding it an exception. 1. Go to the `bootstrap` folder and add the highlighted code below to the `app.php` file. ```php filename="./bootstrap/app.php" copy {14-16} withRouting( web: __DIR__.'/../routes/web.php', commands: __DIR__.'/../routes/console.php', health: '/up', ) ->withMiddleware(function (Middleware $middleware) { $middleware->encryptCookies(except: [ '__prepr_uid', ]); }) ->withExceptions(function (Exceptions $exceptions) { // }) ->create(); ``` 2. Go to the `app/Http/controllers` folder and add the following code to the `PageController.php` to get the `__prepr_uid` cookie value and use it to set the API request header `Prepr-Customer-Id`. ```php filename="./app/Http/controllers/PageController.php" copy {8-9, 13, 22 ,20-22} 'get-page-by-slug', 'variables' => [ 'slug' => $slug, ], 'headers' => [ 'Prepr-Customer-Id' => $request->cookie('__prepr_uid') ] ]); $page = data_get($response->json(), 'data.Page'); // Return the page view with query response if ($page) { return view('pages.index', [ 'page' => $page, ]); } } } ``` ### Test your adaptive content Now that people who visit the Electric lease landing page are being grouped into the *Electric Car Buyers* segment, you can test the adaptive content. To test the adaptive content for the *Electric Car Buyers* segment follow the steps below. 1. Go to the Electric Car landing page in your website. For example: `http://localhost:8000/electric-lease`. 2. Wait a few seconds and go back to the home page. You can now see the personalized home page like in the image below. ![Personalized home page](https://assets-site.prepr.io/50el53oh29zt//personalized-home-page-electric-lease.png) To see the non-personalized page, you can reset the cookies and refresh the page as follows: 3. Right-click the page to **Inspect the page**, click the **Application** tab. Under the cookies, click your localhost. You'll notice the `__prepr_uid` cookie in the list. 4. Right-click the localhost entry to **Clear** the cookies and refresh the page. You should now see the non-personalized content in the home page. ![clear cookies](https://assets-site.prepr.io/2if9ox3qywu6//laravel-home-page-clear-cookies.png) ### Track impressions and conversion events Your personalization is running successfully, however there are no metrics being recorded yet. You can see this with the **Awaiting data** message at the top of the Adaptive content block in the *Homepage* content item. ![Home page adaptive content no metrics](https://assets-site.prepr.io/5tw48qkr5w3h//home-page-content-item-with-adaptive-content-awaiting-data.png) To get Prepr to start showing some metrics data, you need to add HTML attributes to elements in your adaptive content. When you add these attributes, the tracking pixel automatically tracks impressions and your chosen conversion events. 1. To start collecting impressions and clicks, add the HTML attributes `data-prepr-variant-key` and `data-prepr-variant-event` to the *Hero* section as shown in the highlighted code below. The impression is recorded when the *Hero* element scrolls into view. The click is recorded when the button is clicked. ```html filename="./resources/views/components/hero-section.blade.php" copy {1, 12}

{{ data_get($data,'heading') }}

{{ data_get($data,'sub_heading') }}

Hero Image
``` > **Note:** > > If you want to track a conversion other than a click event, e.g. a quote request, then set your `data-prepr-variant-event` to the custom event. > For example: `data-prepr-variant-event="QuoteRequest"`. Check out the [Personalization doc for more details](https://docs.prepr.io/personalization/setting-up-personalization#track-impressions-and-conversions). 2. To start recording metrics, go to the home page in your website, click the **Find your car** button. 3. Now when you view the adaptive content in your *Homepage* content item, you can click the **Metrics** link and see the number of impressions and clicks like in the image below. ![Adaptive content metrics](https://assets-site.prepr.io/6j0q5cpmiu7e//home-page-adaptive-content-metrics-example.png) Congratulations! You have successfully set up personalization with metrics in your Laravel website. This brings you to the end of the Laravel complete guide which has given you all the tools and tips you need to create your own web app connected to Prepr CMS complete with personalization, and A/B testing. Don't hesitate to [give us feedback](mailto:feedback@prepr.io) on your experience using this guide. ## Next steps To learn more on how to expand your project, check out the following resources: - [Set up visual editing](https://docs.prepr.io/project-setup/setting-up-previews-and-visual-editing) - [More data collection details](https://docs.prepr.io/data-collection) - [More about A/B testing](https://docs.prepr.io/ab-testing) - [More about personalization](https://docs.prepr.io/personalization) Source: https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-5-add-personalization --- # Complete guide to Laravel and Prepr *This guide shows you how to connect Prepr to a Laravel project including styling, adaptive content, and A/B testing.* Follow the steps below in the recommended order to set up your own working Laravel website with adaptive content, A/B testing and a preview bar. You can also customize this project to fit the requirements for your web app. - [Introduction](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/introduction): Get a preview of the website you'll build and what you need to get started. - [Set up a Laravel project](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-1-set-up-a-laravel-project): Install and create a working Laravel project with static webpages. - [Make it dynamic](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-2-make-the-project-dynamic): Connect to Prepr using Apollo Client to retrieve and show Acme Lease content. - [Set up data collection](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-3-set-up-data-collection): Set up data collection for A/B testing and personalization. - [Add A/B testing](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-4-add-ab-testing): Set up and see how an A/B test runs in your website. - [Add personalization](https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide/step-5-add-personalization): Deliver a personalized experience to the visitor. Source: https://docs.prepr.io/connecting-a-front-end-framework/laravel/laravel-complete-guide --- # Adding images to web apps *This step-by-step guide describes how to add images to your web application.* ## Introduction Prepr simplifies asset management and provides multi-format support. This guide covers the end-to-end workflow from defining image assets in your Prepr schema, adding images to content items and fetching them via the API. ## Prerequisites You need to have the following setup before you can render images in your web application. - [A free Prepr account](https://signup.prepr.io/) - [At least one content model](https://docs.prepr.io/content-modeling/managing-models#create-a-model) in your environment. > **Note:** > > For more information on how to set up a content model in Prepr, check out the [content modeling basics](https://docs.prepr.io/content-modeling/fundamentals). > If you’re still unsure about how to structure your content, check out our [example models](https://docs.prepr.io/content-modeling/examples) for inspiration. ## Adding images to your web application The steps below make use of the example of a cover image in blog articles. ### Define the *Assets* field in your model To allow editors to add images to content items, you need to add the *Assets* field to the corresponding model as follows: 1. Click the **Schema** tab and click the model you want to update. ![Define asset field in model](https://downloads-site.prepr.io/3m1lx70ujkl7-define-image-asset.gif) 2. Drag the *Assets* field from the list of field types to the position you want it in your model. 3. In our example for a cover image, we choose the **Single-asset field**. This means content editors can only add one cover image to a blog article content item. 4. Enter the **Display name** and make sure only the **Image** asset type is enabled. 5. Click the **Settings** tab to enable the *Caption*, for example, and define the **Asset position and cropping** such as the **Set focal point** option. Then click the **Save** button. For more information on different settings, check out the [Field types guide](https://docs.prepr.io/content-modeling/field-types#assets-field). Now, content editors can add and configure a cover image when they create or edit a blog article content item. ### Add images to a content item Now that you've allowed content editors to add an image to content items for a model, let's look at the typical steps a content editor follows to do this. 1. Navigate to the **Content** tab. You can either [create a new content item](https://docs.prepr.io/content-management/managing-content/managing-content-items#create-a-content-item) or open an existing one. ![Add image to content item](https://downloads-site.prepr.io/5ab3t19d6ndn-add-image-to-content-item.gif) 2. In the content item, you can simply drag an image from your local storage to the image block or choose an existing image in the *Media library*. 3. Add a caption and set the focal point, if needed. The required configuration depends on the [settings](https://docs.prepr.io/content-modeling/field-types#assets-field) defined in the previous step. The content editor can configure the asset with the following options if these are enabled in the settings of the *Assets* field: - [Setting alignment](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets#setting-alignment) to define the position (`left`, `right` or `center`) on a web page. - [Adding captions](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets#adding-captions) to provide a comprehensive description of your images. If you run a multi-language application, you can easily [localize image captions](https://docs.prepr.io/content-management/localizing-content#manage-captions-in-multi-language-content-items). - [Setting an image focal point](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets#setting-an-image-focal-point) to define the key area of an image to remain visible during resizing. - [Cropping images](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets#cropping-images) based on your image presets to adjust visuals to a specific device type or web page element. You can also transform images on the fly using the API. For more details, check out the following references: - [REST API](https://docs.prepr.io/mutation-api/assets-resizing) - [GraphQL API](https://docs.prepr.io/graphql-api/schema-field-types#images) Now that you have content items with images, let's look at how to request those content items with their images using API requests. ### Retrieve images using the API Now that you've added and configured images, you can retrieve them when you request a content item using the API. The following GraphQL query shows how to retrieve image files, including image presets named *"desktop"* and *"mobile"*, image caption, alignment, and author. You may want to add or remove fields according to your desired set-up. For more information, see the [GraphQL API reference](https://docs.prepr.io/graphql-api/schema-field-types#assets). **QUERY** ```graphql copy query { BlogArticle (id: "a9b9266b-35c3-4941-bb02-42c265f16") { _id title _slug # Query the image ("cover") cover { _id url # Example desktop and mobile presets, if configured and set in content item desktopUrl: url(preset: "desktop") mobileUrl: url(preset: "mobile") original_name caption alignment author # Query focal point, if set instead of cropping presets focal_point { x y } height width file_size mime_type } } } ``` **RESPONSE** ```json copy { "data": { "BlogArticle": { "_id": "a9b9266b-35c3-4941-bb02-42c265f16", "title": "Navigating the 2026 Tax Cliff", "_slug": "navigating-the-2026-tax-cliff", // Image details "cover": { "_id": "3a6db09b-b221-403b-978f-815574e0c", "url": "https://acme-v2.stream.prepr.io/1pvvjg1vm/w_3888/climbing-a-cliff.jpg", //cropping preset values, if set and provided, instead of focal point "desktopUrl": "https://acme-v2.stream.prepr.io/1pvvjg1vm/ex_0,ey_974,ew_3888,w_3888,eh_2187,h_2187/climbing-a-cliff.jpg", "mobileUrl": "https://acme-v2.stream.prepr.io/1pvvjg1vm/ex_192,ey_0,ew_3281,w_3281,eh_5832,h_5832/climbing-a-cliff.jpg", "original_name": "climbing a cliff", "caption": "Man climbing a cliff", "alignment": null, "author": null, // x and y are returned, if set, instead of cropping presets "focal_point": { "x": 24, "y": 33 }, "height": 5832, "width": 3888, "file_size": 2192813, "mime_type": "image/jpeg" } } } } ``` > **Note:** > > **Image download filename** > > If you want to make your front-end images available for download in your web application, you can define the download name of the image by > setting the [download filename](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets#setting-a-download-filename) in the URL you provide for download. That's it. Once you [publish your content item](https://docs.prepr.io/content-management/managing-content/managing-content-items#publish-a-content-item), images become available in your web application in the best possible format. ## Want to learn more? Check out the following guides to learn how to add other asset types to your web application: - [Video & Audio](https://docs.prepr.io/development/best-practices/assets/video-audio) - [Live video stream](https://docs.prepr.io/development/best-practices/assets/live-video-stream) - [Files](https://docs.prepr.io/development/best-practices/assets/files) Source: https://docs.prepr.io/development/best-practices/assets/images --- # Adding video and audio to web apps *Follow this guide to learn how to add video and audio files to your web application.* ## Introduction Prepr simplifies asset management and provides multi-format support so you can embed video and audio files in your web application. This guide covers the end-to-end workflow from defining video/audio assets in your Prepr schema, adding these assets to content items and fetching them via the API. ## Prerequisites You need to have the following setup before you can embed video and audio files in your web application. - [A free Prepr account](https://signup.prepr.io/) - [At least one content model](https://docs.prepr.io/content-modeling/managing-models#create-a-model) in your environment. > **Note:** > > For more information on how to set up a content model in Prepr, check out the [content modeling basics](https://docs.prepr.io/content-modeling/fundamentals). > If you’re still unsure about how to structure your content, check out our [example models](https://docs.prepr.io/content-modeling/examples) for inspiration. ## Embedding video and audio files in your web application The steps below make use of the example of including a video or audio file in the content ([*Dynamic content* field](https://docs.prepr.io/content-modeling/field-types#dynamic-content-field)) of blog articles. ### Define the *Assets* field in your model To allow editors to add video or audio to content items, you need to add the *Assets* field to the corresponding model as follows: 1. Click the **Schema** tab and click the model you want to update. ![Add assets to dynamic content field](https://downloads-site.prepr.io/4df5xgflv8qa-define-video-audio.gif) 3. You can simply [add an *Assets* field to your model](https://docs.prepr.io/development/best-practices/assets/images#define-the-assets-field-in-your-model) or as shown in this *Blog article* example, click to open an existing [*Dynamic content* field](https://docs.prepr.io/content-modeling/field-types#dynamic-content-field) and go to the *Settings* tab. 4. Go to the **Assets** section and enable **Video**, **Audio**, and any other settings you need for the video or audio such as **Caption**. For more information on different settings, check out the [Field types guide](https://docs.prepr.io/content-modeling/field-types#assets-field). Now, content editors can add and configure a video or audio when they create or edit a blog article content item. ### Add video or audio to a content item Now that you've allowed content editors to add a video or audio file to content items for a model, let's look at the typical steps a content editor follows to do this. 1. Navigate to the **Content** tab. You can either [create a new content item](https://docs.prepr.io/content-management/managing-content/managing-content-items#create-a-content-item) or use an existing one. ![Add video to content item](https://downloads-site.prepr.io/4ww3m4rdzip0-add-video-to-content-item.gif) 2. You can simply [add a video or audio](https://docs.prepr.io/development/best-practices/assets/images#add-images-to-a-content-item) to your content item or like in this example, go to the [*Dynamic content editor*](https://docs.prepr.io/content-management/managing-content/creating-rich-content#using-the-dynamic-content-editor), click the icon and drag the video or audio file from your local storage to the asset block. 3. You can also configure your video/audio file, if enabled in the settings, by hovering over the video/audio file and clicking the icon in the top right corner, then selecting the needed action. - [**Edit caption**](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets#adding-captions) to describe the video/audio file. If you run a multi-language application, content editors can easily [manage captions in multiple languages](https://docs.prepr.io/content-management/localizing-content#manage-captions-in-multi-language-content-items). - [**Set alignment**](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets#setting-alignment) to define the position of a video/audio file on a web page. 4. You can [add subtitles](https://docs.prepr.io/content-management/managing-assets/managing-assets#adding-video-subtitles) to your videos directly in Prepr (For *Mux Player* only) and [set a custom video thumbnail](https://docs.prepr.io/content-management/managing-assets/managing-assets#replacing-video-thumbnails). Now that you have content items with video or audio files, let's look at how to retrieve them in your front end. ### Retrieve video/audio using the API > **Note:** > > **Mux integration** \ > > All the video and audio files uploaded to Prepr are stored in and played from [Mux](https://prepr.io/integrations/mux). Before you can render any video or audio in your front-end, you need to retrieve these assets using the API. You can retrieve the following fields needed to render video or audio assets: - `playback_id`, the unique ID generated in Mux when a video or audio file is stored in Prepr. - `cover`, if the thumbnail is set for the video asset. - `url`, the video URL in the format: `https://stream.mux.com/{PLAYBACK_ID}.m3u8`. If you use Mux Player to render your video in your front end, the playback\_id is sufficient. For more information on other fields you can include in your query, check out the [GraphQL API reference](https://docs.prepr.io/graphql-api/schema-field-types#video). **QUERY** ```graphql copy query { BlogArticle (id: "36136491-a333-44be-a53f-111ef759779c") { _id title _slug content { __typename ... on Assets { _type name items { _id playback_id url duration caption # Request thumbnail image cover alignment } } } } } ``` **RESPONSE** ```json copy { "data": { "BlogArticle": { "_id": "36136491-a333-44be-a53f-111ef7597", "title": "Navigating the 2026 Tax cliff", "_slug": "navigating-the-2026-tax-cliff", "content": [ { "__typename": "Assets", "_type": "Assets", "items": [ { "_id": "87d4d8ec-c5ac-41cd-a76c-bf2df3fb6", "playback_id": "rLd2ccyWjh02hapHW01tRUFNgYdSx02zIDY200kgKmzj", "url": "https://stream.mux.com/rLd2ccyWjh02hapHW01tRUFNgYdSx02zIDY200kgKmzj.m3u8", "duration": 23000, "caption": "Cars in motion", // Thumbnail image "cover": "https://image.mux.com/rLd2ccyWjh02hapHW01tRUFNgYdSx02zIDY200kgKmzj/thumbnail.jpg", "alignment": null } ] } ] } } } ``` > **Note:** > > Please check out the [Connecting front-end apps guide](https://docs.prepr.io/connecting-a-front-end-framework) to learn more about retrieving content in a specific front-end framework. ### Add a web player to your front end Now that you’ve retrieved the video/audio asset for displaying, you can add your chosen web player to your web application. Because Mux delivers your videos in the *HTTP Live Streaming (HLS)* format, we recommend using a player that supports HLS such as the [Mux Player](https://docs.mux.com/guides/mux-player-web) For other popular web players, check out the [Mux documentation](https://docs.mux.com/guides/play-your-videos#popular-web-players). The instructions below show you how to install and set up the Mux Player. 1. For standard video and audio files, install the Mux player package in your project with the following command line: ```bash copy npm install @mux/mux-player ``` 2. If you're only rendering background videos such as those for more dynamic background hero sections, then install the lightweight [Mux Background Video component](https://www.mux.com/docs/guides/mux-background-video) instead: ```bash copy npm install @mux/mux-background-video ``` 3. Get the `playback_id`, and if needed, the video thumbnail (`cover`) you retrieved in the previous step. > **Note:** > > If [subtitles](https://docs.prepr.io/content-management/managing-assets/managing-assets#adding-video-subtitles) are added to videos in Prepr, these can only be rendered automatically with the *Mux Player*. 4. For video or audio files, you can import the Mux player like in the code snippets below. **Example player components** **MuxVideoPlayer component** ```js copy // components/MuxVideoPlayer.js 'use client'; return ( ); } ``` **MuxAudioPlayer component** ```js copy // components/MuxAudioPlayer.js 'use client'; return ( ); } ``` **MuxBackgroundVideoPlayer component** ```js copy // components/HeroVideo.js 'use client'; return ( {/* Fallback image */} Background video fallback ); } ``` > **Note:** > > For more details on customizing the thumbnail for your Mux video, check out [the Mux docs](https://docs.mux.com/guides/player-customize-look-and-feel#customize-the-poster-image). 5. You can then render the video or audio in the relevant pages in your front end like in the example code snippet below: ```js copy // app/blog/[slug]/page.js const data = await getPreprData(params.slug); return (

{data.title}

); } ``` That's it. Once you [publish your content item](https://docs.prepr.io/content-management/managing-content/managing-content-items#publish-a-content-item), your video or audio file becomes available in your web application in the best possible format. ## Want to learn more? Check out the following guides to learn how to add other asset types to your web application: - [Live video stream](https://docs.prepr.io/development/best-practices/assets/live-video-stream) - [Images](https://docs.prepr.io/development/best-practices/assets/images) - [Files](https://docs.prepr.io/development/best-practices/assets/files) Source: https://docs.prepr.io/development/best-practices/assets/video-audio --- # Adding live video streams to web apps *Follow this guide to learn how to add a live video stream to your web application using Prepr.* ## Introduction Imagine you are about to hold a corporate event. In this case, you may want to create a web page to announce and broadcast your virtual event. Also, recording your online event might be essential, especially if you attract attendees located in another time zone or want to send out the replay to all registrants. Prepr makes all this incredibly simple with the *Live streaming* feature powered by Mux's advanced video services. Prepr works as a centralized interface you can use for the following purposes: - Create and manage a live video stream. - Integrate a live video stream in a web application using the API and a video player. - Save and store your live stream as an on-demand video recording. ## Prerequisites You need to have the following setup before you can render images in your web application. - [A free Prepr account](https://signup.prepr.io/) - [At least one content model](https://docs.prepr.io/content-modeling/managing-models#create-a-model) in your environment. > **Note:** > > For more information on how to set up a content model in Prepr, check out the [content modeling basics](https://docs.prepr.io/content-modeling/fundamentals). > If you’re still unsure about how to structure your content, check out our [example models](https://docs.prepr.io/content-modeling/examples) for inspiration. ## Adding live streams to your web application The steps below make use of the example of a live stream in a blog article. ### Define the *Assets* field in your model To allow editors to add live streams to content items, you need to add the *Assets* field to the corresponding model: 1. Click the **Schema** tab and click the model you want to update. 2. Drag the *Assets* field from the list of field types to the position you want it in your model. 3. In our example, we choose the **Single-asset field**. This means content editors can only add one live stream to a blog article content item. 4. Enter the **Display name** and make sure the **Video** asset type is enabled. 5. Click the **Settings** tab to enable the *Caption*, for example. Then click the **Save** button. For more information on different settings, check out the [Field types guide](https://docs.prepr.io/content-modeling/field-types#assets-field). Now, you can set up a live stream so editors can add a live stream to a blog article content item. ### Set up a live stream in Prepr 1. Go to the **Media** tab and in the **Upload asset** button, click to open the drop-down menu and choose the **Add live stream** option. ![Set up a live stream in Prepr](https://downloads-site.prepr.io/4n7skti3tclx-set-up-live-stream-in-prepr.gif) 2. In the pop-up dialog window, give your livestream asset a name and click the **Add** button to generate a unique stream key for your broadcast. 3. The next screen gives you the *Server URL* and *Stream key* that you need to [connect to the broadcast software](https://docs.prepr.io/development/best-practices/assets/live-video-stream#configure-your-broadcast-software): - *Server URL* is the URL of the Mux RTMP server used to connect your broadcast software to Mux’s entry point for live streams. - *Stream key* is your secret key used to authenticate your live stream with the Mux RTMP server in your broadcast software. > **Warning:** > > The stream key is shown only once. Make sure to save it securely. 4. You can [add subtitles](https://docs.prepr.io/content-management/managing-assets/managing-assets#adding-video-subtitles) to your live stream directly in Prepr (For *Mux Player* only) and [set a custom video thumbnail](https://docs.prepr.io/content-management/managing-assets/managing-assets#replacing-video-thumbnails). Once done, content editors can add the livestream asset to a blog article content item. ### Add live stream to a content item Now that you've set up a live stream, let's look at the typical steps a content editor follows to add it to a blog article content item. 1. Navigate to the **Content** tab. You can either [create a new content item](https://docs.prepr.io/content-management/managing-content/managing-content-items#create-a-content-item) or use an existing one. ![Add live stream to content item](https://downloads-site.prepr.io/y5haxb8vtrl-add-live-stream-to-content-item.gif) 2. On the **Content item** page, click the *Live stream* field to open the *Media Library*. 3. Select the livestream asset and click the **Add 1 asset** button. 4. To make the content available in a web application, click the **Publish** button. Now that the content is available, you can configure your preferred broadcast software. ### Configure your broadcast software In this step, you need to configure your broadcast software using the *Stream key* and *Server URL* you copied when you created the live stream. Broadcast software uses video encoding technology to convert your video feed into a digital format suitable for live streaming across various devices and platforms. Here’s the list of broadcast solutions you may want to consider: - [OBS](https://obsproject.com/) (free and open source, macOS/Windows/Linux) - [Wirecast](https://www.telestream.net/wirecast/) (commercial, macOS/Windows) - [Larix Broadcaster](https://apps.apple.com/us/app/larix-broadcaster/id1042474385) (free, iOS) - [Wirecast Go](https://apps.apple.com/us/app/wirecast-go/id987286934) (commercial, iOS) The below example uses OBS configuration. For more details, check out the [OBS documentation](https://obsproject.com/wiki/OBS-Studio-Overview). ![Configure your broadcast software](https://assets-site.prepr.io/w_1920/4aqejqwjkx9c-configure-broadcast-software.png) Once the broadcasting software is set up, you can add a web player to your front end. ### Retrieve the live stream using the API Use the following snippets as a starting point to retrieve your live stream via the API. You may want to add or remove fields according to your desired setup. For more information, see the [GraphQL API reference](https://docs.prepr.io/graphql-api/schema-field-types#video). > **Note:** > > To integrate your live stream with a 3rd party player, make sure to include the `url` field in your API request. > It will return a video URL in the format: `https://stream.mux.com/{PLAYBACK_ID}.m3u8`. > For Mux Player, including the `playback_id` is sufficient. **QUERY** ```graphql copy query { BlogArticle (id: "a75f79e6-b11f-4f9e-a173-de58856a7") { _id title _slug live_stream { _id name url playback_id caption alignment } } } ``` **RESPONSE** ```json copy { "data": { "BlogArticle": { "_id": "a75f79e6-b11f-4f9e-a173-de58856a7", "title": "Join the biggest event this year: Auto show 2026", "_slug": "join-the-biggest-event-this-year-auto-show-2026", "live_stream": { "_id": "337b3197-b348-4dd1-bb48-3c99f6ecc", "name": "Auto show 2026", "url": "https://stream.mux.com/iCcDOgaw85OzFTZySQOnf6PilVla5c00dizP7QUYc.m3u8", "playback_id": "iCcDOgaw85OzFTZySQOnf6PilVla5c00dizP7QUYc", "caption": "Watch the Auto show live stream", "alignment": null } } } } ``` > **Note:** > > Please check out the [Connecting front-end apps guide](https://docs.prepr.io/connecting-a-front-end-framework) to learn more about retrieving content in a specific front-end framework. Now that you have the live stream details, you can implement the web player with these values. ### Add a web player to your front end You’ve prepared a livestream asset, configured your broadcast software, and retrieved the live stream details. Now you can choose which web player to use in your web application. Because Mux delivers your videos in the *HTTP Live Streaming (HLS)* format, we recommend using a player that supports HLS. Here are some popular players you may want to use: - [Mux Player](https://docs.mux.com/guides/mux-player-web) is a native player developed by Mux. - [HLS.js](https://github.com/video-dev/hls.js) is free and open source. HLS.js will be a good choice if you want to use the HTML5 video element's default controls or build your own UI elements. - [Agnoplay](https://www.agnoplay.com/) is a fully agnostic, cloud-based player solution for web, iOS, and Android. - [JWPlayer](https://www.jwplayer.com/html5-video-player/) is a commercial player that supports HLS by default and uses HLS.js as an underlying HLS engine. Check out other options in the [Mux documentation](https://docs.mux.com/guides/play-your-videos#popular-web-players). To add the *Mux Player* to your web app, go to the **Media** tab and open the livestream asset. On the livestream **Asset** detail page, go to the **Embed code** field, to find the sample code you can include in your web app. Check out the [Mux player docs](https://www.mux.com/docs/guides/play-your-videos#mux-video-element) for HTML and React implementation details. > **Note:** > > Before your live video stream starts, the playback URL will be inactive, therefore, no content will be available in a web player. > In that case, we recommend setting up an intro layout, for example, the countdown timer or image cover, etc., to give your live stream a professional look and to grab the audience's attention. > Refer to your web player documentation for more details. ### Play your live stream Follow your broadcast software instructions to play the live stream. Some general recommendations are listed below. **Start a live stream** Once the live stream begins, it becomes available in your web application instantly. At the same time, the software starts pushing live video to the video host server. ![Start streaming](https://assets-site.prepr.io/w_1920/6329av8ziyi-screenshot-2022-10-05-at-123729.png) You can also see the *Live* label in the livestream asset in Prepr: ![Livestream asset in Prepr](https://assets-site.prepr.io/3xcv683veog6//live-icon-while-streaming.png) **Stop a live stream** Refer to your broadcast software documentation on how to stop a live stream. Please note there may be a slight delay between when you stop streaming in the broadcast software and when the recording stops in Prepr. To stop recording immediately, we recommend that you follow this sequence of actions: 1. First, stop recording your live stream in Prepr. For that, go to the **Media** tab and open the livestream asset. On the livestream *Asset* detail page, click the **Finish live stream** button. ![Stop streaming](https://assets-site.prepr.io/3au42ub37jze//stop-streaming-button.png) 2. Then, stop streaming in your broadcast software. > **Warning:** > > Make sure to stop streaming within the [60s timeframe](https://docs.mux.com/guides/reduce-live-stream-latency#reconnect-window) after you stop recording in Prepr. If not, your broadcast software may re-establish the connection, resulting in a new recorded asset. Once done, the live stream will be replaced with the video recording in your web application automatically. Alternatively, you can access and play this video recording in the *Media Library*. Because Prepr keeps your *Stream key* active, the livestream asset also remains available in the *Media Library*. So you can start a new live stream using the same credentials anytime. In this case, you’ll get several video recordings of your live stream in the *Media Library*, and your web page will show the latest recording every time. ## Want to learn more? Check out the following guides to learn how to add other asset types to your web application: - [Images](https://docs.prepr.io/development/best-practices/assets/images) - [Video & Audio](https://docs.prepr.io/development/best-practices/assets/video-audio) - [Files](https://docs.prepr.io/development/best-practices/assets/files) Source: https://docs.prepr.io/development/best-practices/assets/live-video-stream --- # Adding files to web apps *This guide describes adding different file formats such as PDF, ZIP, GIF, and more to your web application using Prepr.* ## Introduction Prepr allows you to add several file formats to a web application. You can use popular file types like `PDF` and `ZIP`, for example, to publish a *Press kit*, *Terms of use*, or any other document you want your website visitors to download. ## Prerequisites You need to have the following setup before you can embed downloadable files in your web application. - [A free Prepr account](https://signup.prepr.io/) - [At least one content model](https://docs.prepr.io/content-modeling/managing-models#create-a-model) in your environment. > **Note:** > > For more information on how to set up a content model in Prepr, check out the [content modeling basics](https://docs.prepr.io/content-modeling/fundamentals). > If you’re still unsure about how to structure your content, check out our [example models](https://docs.prepr.io/content-modeling/examples) for inspiration. ## Adding downloadable files to your web application The steps below make use of the example of downloadable files in blog articles. ### Define the *Assets* field in your model To allow editors to add files to content items, you need to add the *Assets* field to the corresponding model as follows: 1. Click the **Schema** tab and click the model you want to update. ![Define file asset field in model](https://downloads-site.prepr.io/3r4husts2okr-define-file-asset.gif) 2. Drag the *Assets* field from the list of field types to the position you want it in your model. 3. Following this example, choose the **Multi-asset field**. This means content editors can add multiple files to a blog article content item. 4. Enter the **Display name** and make sure only the **File** asset type is enabled. 5. Click the **Settings** tab to enable the *Caption*, for example. Then click the **Save** button. For more information on different settings, check out the [Field types guide](https://docs.prepr.io/content-modeling/field-types#assets-field). Now, content editors can add and configure a file when they create or edit a blog article content item. ### Add files to a content item Now that you've allowed content editors to add files to content items for a model, let's look at the typical steps a content editor follows to do this. 1. Navigate to the **Content** tab. You can either [create a new content item](https://docs.prepr.io/content-management/managing-content/managing-content-items#create-a-content-item) or open an existing one. ![add files to content item](https://downloads-site.prepr.io/4mezr31wwsy7-add-files-to-content-item.gif) 2. In the content item, you can simply drag multiple files from your local storage to the asset block or choose existing files in the *Media library*. 3. Add a caption, if needed. The required configuration depends on the [settings](https://docs.prepr.io/content-modeling/field-types#assets-field) defined in the previous step. The content editor can configure the asset with the following options if these are enabled in the settings of the *Assets* field: - [Setting alignment](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets#setting-alignment) to define the position (`left`, `right` or `center`) on a web page. - [Adding captions](https://docs.prepr.io/content-management/managing-assets/editing-and-configuring-assets#adding-captions) to provide a comprehensive description of your files. If you run a multi-language application, you can easily [localize image captions](https://docs.prepr.io/content-management/localizing-content#manage-captions-in-multi-language-content-items). Now that you have content items with downloadable files, let's look at how to request those content items and files using API requests. ### Retrieve files using the API Now that you have content items with files, let's look at how to request those content items and files using API requests. The following GraphQL query shows how to retrieve downloadable files. You may want to add or remove fields according to your desired set-up. For more information, see the [GraphQL API reference](https://docs.prepr.io/graphql-api/schema-field-types#assets). **QUERY** ```graphql copy query { BlogArticle (id: "36136491-a333-44be-a53f-111ef7597") { _id title _slug downloads { _id url caption alignment mime_type } } } ``` **RESPONSE** ```json copy { "data": { "BlogArticle": { "_id": "36136491-a333-44be-a53f-111ef7597", "title": "Navigating the 2026 Tax cliff", "_slug": "navigating-the-2026-tax-cliff", "downloads": [ { "_id": "63d1149a-551e-4364-9f8e-2cf6f75a3", "url": "https://acme-v2.files.prepr.io/7cqftbpvtt6y/2026-tax-checklist.pdf", "caption": "2026 Tax checklist", "alignment": null, "mime_type": "application/pdf" }, { "_id": "f818eb93-6e1b-4e3b-a45e-07197a911", "url": "https://acme-v2.files.prepr.io/j6wu2efdq17/2026-tax-rates.pdf", "caption": "2026 Tax rates", "alignment": null, "mime_type": "application/pdf" }, { "_id": "76b4e524-8038-4741-be30-7057a3486", "url": "https://acme-v2.files.prepr.io/tkvmdj4u9l7/2026-tax-plan.pdf", "caption": "2026 Tax plan", "alignment": null, "mime_type": "application/pdf" } ] } } } ``` > **Note:** > > Please check out the [Connecting front-end apps guide](https://docs.prepr.io/connecting-a-front-end-framework) to learn more about retrieving content in a specific front-end framework. That's it. Once you [publish your content item](https://docs.prepr.io/content-management/managing-content/managing-content-items#publish-a-content-item), your file becomes available in your web application for download. ## Want to learn more? Check out the following guides to learn how to add other asset types to your web application: - [Images](https://docs.prepr.io/development/best-practices/assets/images) - [Video & Audio](https://docs.prepr.io/development/best-practices/assets/video-audio) - [Live video stream](https://docs.prepr.io/development/best-practices/assets/live-video-stream) Source: https://docs.prepr.io/development/best-practices/assets/files --- # Working with assets Follow these guidelines to help you include images, videos, audios, and files in your web applications. - [Images](https://docs.prepr.io/development/best-practices/assets/images): Explore how to add images to your web application using Prepr. - [Video & Audio](https://docs.prepr.io/development/best-practices/assets/video-audio): Learn how to post videos and audios to your web application from Prepr. - [Live video stream](https://docs.prepr.io/development/best-practices/assets/live-video-stream): Get a walkthrough on streaming your live video in a web application using Prepr. - [Files](https://docs.prepr.io/development/best-practices/assets/files): Check out how to include files in your web application with Prepr. Source: https://docs.prepr.io/development/best-practices/assets