Saltar al contenido principal

Guía de integración Clione + Hydrogen (Remix)

Pinta las señales SEO de Clione en un storefront Shopify Hydrogen, server-side, en la primera respuesta.

@clione/seo/remix te da los mismos dos bloques que pinta la theme app extension de Shopify (el @graph JSON-LD y el acordeón de FAQ visible) como componentes React, alimentados por los mismos metafields.

Instalar​

npm install @clione/seo

react (que ya está en cualquier app Hydrogen) es la única peer dependency que necesita este adaptador.

Opción A: modo metafield (recomendado)​

Hydrogen ya consulta la Storefront API de Shopify. Añade los campos de Clione a esa query: sin llamada extra a la API y sin API key de Clione.

1. Añade los campos de Clione a tu query​

CLIONE_STOREFRONT_FIELDS selecciona seo { title description } y los metafields clione.jsonld y clione.faq_html:

import { CLIONE_STOREFRONT_FIELDS } from '@clione/seo/remix'

const PRODUCT_QUERY = `#graphql
query ClioneProduct($handle: String!, $country: CountryCode, $language: LanguageCode)
@inContext(country: $country, language: $language) {
product(handle: $handle) {
id
title
handle
descriptionHtml
${CLIONE_STOREFRONT_FIELDS}
}
}
`

El meta title y la descripción que escribe Clione viven en global.title_tag / global.description_tag de Shopify. La Storefront API no devuelve el namespace global a través de metafields(identifiers:); seo { title description } es como los lee un storefront, y ya va en la selección. En colecciones y páginas, el meta title solo se escribe si Publicar meta_title en categorías, colecciones y páginas está activado (Ajustes de la tienda → General); en productos se escribe siempre.

CLIONE_STOREFRONT_FIELDS solo lee los metafields clione.jsonld y clione.faq_html, así que en un mercado traducido el JSON-LD y las FAQs que pinta este adaptador se quedan en el idioma principal de la tienda.

2. Carga las señales y píntalas​

// app/routes/products.$handle.tsx
import { json, type LoaderFunctionArgs, type MetaFunction } from '@shopify/remix-oxygen'
import { useLoaderData } from '@remix-run/react'
import { ClioneFaq, ClioneJsonLd, clioneMeta, loadClioneSignals } from '@clione/seo/remix'

export async function loader({ params, context }: LoaderFunctionArgs) {
const { product } = await context.storefront.query(PRODUCT_QUERY, {
variables: { handle: params.handle },
})
if (!product) throw new Response('Not found', { status: 404 })

// Metafield mode: no network call. The signals are plain JSON.
return json({ product, seo: loadClioneSignals(product) })
}

export const meta: MetaFunction<typeof loader> = ({ data }) =>
clioneMeta(data?.seo, { title: data?.product.title })

export default function ProductPage() {
const { product, seo } = useLoaderData<typeof loader>()
return (
<main>
{/* A JSON-LD script is valid anywhere in the document. */}
<ClioneJsonLd signals={seo} />
<h1>{product.title}</h1>
<div dangerouslySetInnerHTML={{ __html: product.descriptionHtml }} />
{/* Where you want the visible FAQ accordion. */}
<ClioneFaq signals={seo} />
</main>
)
}

Lo que esto mete en la primera respuesta HTML, sin ningún script de Clione:

  • <title> y <meta name="description"> a partir de seo { title description }, mediante meta().
  • <script type="application/ld+json"> con el grafo de clione.jsonld tal cual (Organization + WebSite + BreadcrumbList + Product, más FAQPage si hay FAQs).
  • El acordeón .clione-faq de clione.faq_html, tal cual.
  • <meta name="clione:enriched" content="true">, que la extensión Clione Pulse usa para reconocer un storefront headless.

Tu propio breadcrumb​

Si la ruta ya pinta un BreadcrumbList (JSON-LD, microdata o RDFa), díselo a <ClioneJsonLd> para que deje fuera el de Clione: dos breadcrumbs en una misma URL entran en conflicto.

<ClioneJsonLd signals={seo} nativeStructuredData={true} />

nativeStructuredData también acepta el HTML de tu breadcrumb como string. Las plantillas por defecto de Hydrogen no pintan ningún breadcrumb, así que un storefront de serie no necesita nada.

Pintar el <head> desde un componente​

Si tu layout pinta el <head> desde un componente en vez de con el export meta() de la ruta, usa <ClioneHead signals={seo} fallbackTitle={product.title} />: título, descripción, la marca y el JSON-LD de una vez. No lo combines con clioneMeta() o el título sale dos veces.

Opción B: modo API​

Si prefieres no tocar tus queries de la Storefront API, trae las señales de Clione durante el SSR. Necesitas una API key de Clione de tipo server; mira Autenticación.

// app/routes/products.$handle.tsx
import { json, type LoaderFunctionArgs, type MetaFunction } from '@shopify/remix-oxygen'
import { useLoaderData } from '@remix-run/react'
import { createClioneSeo } from '@clione/seo'
import { ClioneFaq, ClioneJsonLd, clioneMeta } from '@clione/seo/remix'

export async function loader({ params, context }: LoaderFunctionArgs) {
const clione = createClioneSeo({
source: 'api',
apiUrl: context.env.CLIONE_API_URL,
apiKey: context.env.CLIONE_API_KEY,
platform: 'shopify',
storefrontHost: 'shop.example.com', // a host on the key's allowed domains
})

const [{ product }, seo] = await Promise.all([
context.storefront.query(PRODUCT_QUERY, { variables: { handle: params.handle } }),
clione.getSignals('product', params.handle!), // a Shopify handle works as the id
])

return json({ product, seo })
}

export const meta: MetaFunction<typeof loader> = ({ data }) =>
clioneMeta(data?.seo, { title: data?.product.title })

El componente es el mismo que en la opción A. getSignals nunca lanza una excepción ante un error de red o HTTP: devuelve señales vacías y la página se pinta sin ellas.

Variables de entorno​

# API mode only
CLIONE_API_URL=https://api.clione.ai
CLIONE_API_KEY=sk_live_...

Por qué el modo metafield es mejor para Hydrogen​

Hydrogen ya hace una llamada a la Storefront API en cada página. Añadir los campos de Clione a esa query no cuesta ninguna petición extra. El modo API añade una segunda llamada de red durante el SSR: rápida y cacheable, pero innecesaria cuando los datos ya están en Shopify.

Tipos de entidad soportados​

Productos, colecciones y páginas llevan los mismos metafields clione.jsonld / clione.faq_html, así que el loader y los componentes son idénticos: selecciona CLIONE_STOREFRONT_FIELDS dentro de collection(handle:) o page(handle:) en lugar de product(handle:).