Prismic Preview not working after localization

I am currently working on localization for my project.

I closely followed this guide https://prismic.io/blog/nextjs-i18n to achieve localization and while I have got the localization working, the routing has been affected since now my public folder is not accessible in my dev environment and PrismicPreview is not working.

My localization code is almost identically set up like the guide suggests so if anyone has encountered this before, I am all ears for a possible solution.

Please let me know if you need any code snippets.

Hi @prismic31 ,

Have you tried updating the imports in the PrismicPreview component? Specially the import fo the route resolver?

Thanks.

Hi @Phil ,
I´m currently using the PrismicPreview component from @prismicio/next and the package is up to date.

The PrismicPreview toolbar shows up, indicating that the preview is working but when I click the "Preview the page", I am directed to the base "not found" page given by NextJS in your codebase when scaffolding a project.

This behaviour tell me that likely the PrismicPreview component itself is not the problem but the way it is handling the new route setup.

Currently, my base route of localhost:3000 has a localization affix of /en-gb (which is not normally visible because it is the default locale) and /fr (which is visible when French is selected) so all my routes in french would be {localhost}/fr/{page} and all my routes in english would be {localhost}/{path}.

Is this commonly seen issue when using previews on locale prefixed routes?

OK, in that case, the route/link resolver is used to redirect to the correct page. So if there's a disconnect between the app folder structure and the link resolver, your previews will break down.

Can you show me the code for your link resolver and your preview component?

linkResolver.ts

import {
    FilledContentRelationshipField,
    FilledLinkToMediaField,
    FilledLinkToWebField,
    LinkType,
} from '@prismicio/types';
import { PrismicLinkWithMeta } from './types/link';
import { RelatedDocument } from './prismic-types';
import { LinkField } from '@prismicio/client';

/**
 * Optional base path, e.g. if everything prismic related is at `/prismic`
 * set as `prismic/`, otherwise leave as empty string.
 */
const BASE_PATH = '';

/** The types we can link resolve, add any project specific types here */
export type LinkResolvable =
    | (FilledLinkToWebField & {
          _linkType?: string;
      })
    | (FilledLinkToMediaField & {
          _linkType?: string;
      })
    | (FilledContentRelationshipField & {
          _linkType?: string;
      })
    | PrismicLinkWithMeta
    | LinkField
    | null;

/** Possible values for normalized Prismic links */
export type LinkType = keyof typeof LinkType;

/** Normalize different representation of links in Prismic into one type */
export type NormalizedLink = {
    /** Prismic returns this as either link_type or _linkType */
    linkType?: LinkType | string;
    uid?: string;
    /** This is the document type, e.g. "frontpage", "page", "article" */
    type?: string;
    url?: string;
    locale?: string;
};

/**
 * When adding links via the Prismi UI, it's possible to paste in relative links (e.g. `/my-page`).
 * In those cases the UI will append `https://` in front, so we end up with `https:///my-page`.
 *
 * @param url Possible url from the Prismic UI
 * @returns Url with protocol stripped
 */
function fixRelative(url?: string): string {
    if (!url) {
        return '/';
    }

    const lowered = url.toLocaleLowerCase();

    // Prismic UI will allow OR add https:// for relative links in their UI
    if (lowered.startsWith('https:///')) {
        return url.substring('https://'.length);
    }

    if (lowered.startsWith('http:///')) {
        return url.substring('http://'.length);
    }

    return url;
}

/**
 * Resolves a link to content based on its type.
 *
 * @param type Name of the content type in Prismic
 * @param uid UID of content
 * @param localeAsString Locale of content
 * @returns Resolved link to the content based on input
 */
export function linkResolverByType(
    type?: string,
    uid?: string,
    lang?: string
): string {

    const localeHref = `${lang}${BASE_PATH}`;

    switch (type) {
        case 'frontpage':
        case 'layout':
            return localeHref;
        default:
    }

    if (uid) {
        return `${localeHref}${uid}`;
    }

    return "/";
}

/**
 * When linking between content there are two possible ways Prismic represents it:
 * - As link in RichText content
 * - As link in "Linked Content" UI element
 * This handles both types.
 *
 * @param link Normalized link to resolve
 * @returns Resolved link to the content based on its link object
 */
export function linkResolverByLink(
    link: NormalizedLink
): string {
    if (!link.linkType) {
        return '/';
    }

    // Possible values for link_type
    if (
        ['Media', 'Web', 'File', 'Image', 'Document'].indexOf(
            link.linkType
        ) >= 0
    ) {
        return fixRelative(link.url);
    }

    // Possible values for _linkType
    if (
        [
            'Link.media',
            'Link.web',
            'Link.file',
            'Link.image',
            'Link.Document'
        ].indexOf(link.linkType) >= 0
    ) {
        return fixRelative(link.url);
    }

    return '/';
}

/**
 * Checks the shape of the document to link resolve to and creates a link to it.
 *
 * @param doc Prismic document to resolve a link to
 * @returns Link to document
 */
export function linkResolver(
    doc?: LinkResolvable
): string {

    if (!doc || typeof doc !== 'object') {
        return '/';
    }


    // Preview and content relationship links
    if ('link_type' in doc && 'uid' in doc) {
        return linkResolverByLink({
            linkType: doc.link_type,
            uid: doc.uid,
            type: doc.type,
            url: doc.url,
            locale: doc.lang,
        });
    }

    // Media and Web links
    if ('url' in doc) {
        return linkResolverByLink({
            linkType: doc.link_type,
            url: doc.url,
        });
    }

    return '/';
}

/**
 * Tries to resolve a link to a related document that comes
 * from `_meta.alternateLanguages`
 * @param doc Related prismic document.
 * @returns Link to document if able to resolve, otherwise `/`.
 */
export function linkResolverByRelatedDocument(
    doc?: RelatedDocument
) {
    if (!doc || !doc.uid) {
        return '/';
    }

    return linkResolverByType(
        doc.type,
        doc.uid,
        doc.lang
    );
}

My PrismicPreview componentis imported from the v1.5.0 of the "@prismicio/next" module and is used in my layout.tsx like this:

import { Poppins } from 'next/font/google'
import './globals.css'
import { PrismicPreview } from '@prismicio/next'
import config from "../../slicemachine.config.json";
import { ApolloWrapper } from './ApolloWrapper';

const repositoryName = config.repositoryName;

const poppins = Poppins({ weight: "400", subsets: ['latin-ext'] })

export default function RootLayout({
  	children,
}: {
  	children: React.ReactNode
}) {
	return (
		<html lang="en">
			<PrismicPreview repositoryName={repositoryName}>
				<body className={poppins.className}>
					<ApolloWrapper>{children}</ApolloWrapper>
				</body>
			</PrismicPreview>
		</html>
  	)
}

Thank you for sending this.

In general, are you're having no problems with your links and localisation outside of previews?

I have figured out a solution to this problem and it is very simple.

In this guide, you'll have a [lang] folder and if you want to have previews available, you'll have an api folder.

Since localization will be appended to your base route (/ if it is your default locale and in my case, /fr since french is one of my chosen locales), you´ll want to move the api folder to the [lang] so instead of http://localhost:3000/api/preview, you'll get http://localhost:3000/fr/api/preview.

This simple fix solved my problem so this ticket can be closed.

2 Likes

Thanks Jón,

I'll check with the team about updating the documentation for this.