Cómo crear un componente de React personalizado para representar etiquetas de metadatos de SEO y qué etiquetas incluir para mejorar la clasificación y el rendimiento en la web.
Primeros pasos
Para este tutorial, vamos a utilizar CheatCode Next.js Boilerplate como punto de partida para nuestro trabajo. Para empezar, clonemos una copia de Github:
Terminal
git clone https://github.com/cheatcode/nextjs-boilerplate
A continuación, cd
en el proyecto e instalar sus dependencias:
Terminal
cd nextjs-boilerplate && npm install
Finalmente, continúe e inicie el servidor de desarrollo:
Terminal
npm run dev
Con eso, estamos listos para comenzar.
Creación de un componente SEO
Para este tutorial, vamos a crear un componente React que nos ayudará a representar nuestros datos de SEO dentro de Next.js. Esto nos facilitará agregar metadatos de SEO a cualquier página, solo teniendo que cambiar los accesorios que le pasamos al componente.
/componentes/SEO/index.js
import React from "react";
import PropTypes from "prop-types";
import Head from "next/head";
import settings from "../../settings";
const SEO = (props) => {
const { title, description, image } = props;
return (
<Head>
<title>{title} | App</title>
<meta name="description" content={description} />
<meta itemprop="name" content={title} />
<meta itemprop="description" content={description} />
<meta itemprop="image" content={image} />
</Head>
);
};
SEO.defaultProps = {
title: settings && settings.meta && settings.meta.title,
description: settings && settings.meta && settings.meta.description,
image:
settings &&
settings.meta &&
settings.meta.social &&
settings.meta.social.graphic,
};
SEO.propTypes = {
title: PropTypes.string,
description: PropTypes.string,
image: PropTypes.string,
};
export default SEO;
Centrándonos cerca de la parte superior del archivo, para nuestro componente React, vamos a usar el patrón del componente de función ya que no necesitamos acceso a ningún método de ciclo de vida o estado para este componente.
En la parte superior del archivo, importamos otro componente React del next/head
paquete, <Head />
. Este es un componente que está integrado en Next.js y nos ayuda a definir los datos que se mostrarán (automáticamente por Next.js) en el <head></head>
etiquetas del HTML para la página donde se representa este componente.
Para el valor de retorno de nuestro componente—<SEO />
—representamos este <Head />
componente como una etiqueta de apertura y cierre. Esto significa para React que el contenido entre la etiqueta de apertura y cierre son elementos secundarios de ese componente. Aunque no podemos verlo, React tiene un accesorio estándar children
que se le asigna el marcado pasado entre una etiqueta de apertura y cierre como vemos arriba. Internamente, el <Head />
componente lee este children
prop y lo usa para llenar el <head></head>
etiqueta en el HTML representado para la página (lo que se envía de vuelta a Google y otros motores de búsqueda).
Entre esas etiquetas, pasamos un HTML estándar <title />
etiqueta junto con una serie de <meta />
etiquetas Aunque estamos en un componente React, este marcado representa HTML simple. Si tuviéramos que copiar estas etiquetas y pegarlas en el <head></head>
de un simple .html
archivo, el navegador los mostraría sin problema.
Aquí, debido a que estamos dentro de React, o, más correctamente, JSX, el lenguaje de marcado que usa React, podemos pasar valores dinámicos (aquí, conocidos como expresiones de React) a los atributos de estas etiquetas usando llaves. En el código anterior, justo dentro del cuerpo de la función de nuestro componente, estamos usando la desestructuración de objetos de JavaScript para "arrancar" el title
, description
y image
anticipamos ser pasados a nuestro <SEO />
componente.
Suponiendo que estos estén definidos, los establecemos en nuestro marcado de metadatos usando expresiones React, representando el title
en el <title></title>
etiqueta y los demás como el content
atributo de sus respectivos <meta />
etiquetas Es importante tener en cuenta aquí:debido a que estamos tratando de cubrir todas nuestras bases por el bien del SEO, veremos que los datos se pasan varias veces a diferentes etiquetas. Esto es intencional. Esto se debe a que diferentes motores de búsqueda analizarán nuestros datos de diferentes maneras. Al hacer esto, estamos asegurando la máxima compatibilidad para nuestro contenido.
Hacia el final de nuestro archivo, notaremos que estamos aprovechando el .defaultProps
de React y .propTypes
atributos de nuestro componente. El último, .propTypes
, está destinado a ayudarnos a validar el contenido de los accesorios que se nos pasan. Esto es para nosotros como desarrolladores y no afecta a nuestros usuarios. Aquí, usando el PropTypes
objeto que hemos importado arriba, establecemos la expectativa de nuestros tres accesorios title
, description
y image
todos contienen un valor de cadena (designado en nuestro código como PropTypes.string
).
Justo encima de esto, también definimos algunos defaultProps
para nuestro <SEO />
componente. Esto es importante. Tenga en cuenta que aquí estamos accediendo a un valor settings
que asumimos es un objeto importado de otra parte de nuestro proyecto. En el modelo que clonamos al comienzo del tutorial, existe una convención para cargar un archivo de configuraciones arbitrarias basadas en el entorno actual o el valor de process.env.NODE_ENV
. Por defecto, este valor es development
por lo que esperamos que el repetitivo haya cargado el contenido del /settings/settings-development.js
archivo para nosotros.
/configuración/configuración-desarrollo.js
const settings = {
graphql: {
uri: "http://localhost:5001/api/graphql",
},
meta: {
rootUrl: "http://localhost:5000",
title: "App",
description: "The app description goes here.",
social: {
graphic:
"https://cheatcode-assets.s3.amazonaws.com/default-social-graphic.png",
twitter: "@cheatcodetuts",
},
},
routes: {
authenticated: {
pathAfterFailure: "/login",
},
public: {
pathAfterFailure: "/documents",
},
},
};
export default settings;
Cabe destacar que veremos que en esta configuración, un meta
object se establece en una serie de pares clave/valor. Estos datos se configuran como los metadatos de SEO predeterminados para todo nuestro sitio (o, dicho de otra manera, los datos de respaldo en los que confiaremos si no pasamos ningún valor para los accesorios de nuestro <SEO />
componente).
/componentes/SEO/index.js
import React from "react";
import PropTypes from "prop-types";
import Head from "next/head";
import settings from "../../settings";
const SEO = (props) => {
const { title, description, image } = props;
return (
<Head>
<title>{title} | App</title>
<meta name="description" content={description} />
<meta itemprop="name" content={title} />
<meta itemprop="description" content={description} />
<meta itemprop="image" content={image} />
</Head>
);
};
SEO.defaultProps = {
title: settings && settings.meta && settings.meta.title,
description: settings && settings.meta && settings.meta.description,
image:
settings &&
settings.meta &&
settings.meta.social &&
settings.meta.social.graphic,
};
SEO.propTypes = {
title: PropTypes.string,
description: PropTypes.string,
image: PropTypes.string,
};
export default SEO;
De vuelta en nuestro componente, podemos ver que estamos extrayendo ese archivo de configuración y, en nuestro .defaultProps
objeto, pasando el contenido del meta
objeto en ese archivo. Nuevamente, esto asegura que si no pasa estos accesorios, tendremos algunos datos pasados en lugar de una cadena vacía o un valor "indefinido".
Adición de etiquetas de metadatos para redes sociales
Si bien el código que vimos anteriormente sin duda nos ayudará a comenzar con nuestras necesidades de SEO, en términos web modernos, eso sería como llevar un cuchillo a un tiroteo. Debido a que la web ha proliferado en diferentes redes sociales y algoritmos de motores de búsqueda cada vez más complejos, ayuda a nuestro caso en aras de la clasificación para agregar datos más específicos.
En particular, queremos agregar soporte para datos sociales de dos grandes sitios:Twitter y Facebook. Afortunadamente, aunque necesitaremos admitir más etiquetas, la estructura de esas etiquetas es lo suficientemente similar como para que podamos automatizar la mayor parte de su salida.
/componentes/SEO/index.js
import React from "react";
import PropTypes from "prop-types";
import Head from "next/head";
import settings from "../../settings";
const socialTags = ({
openGraphType,
url,
title,
description,
image,
createdAt,
updatedAt,
}) => {
const metaTags = [
{ name: "twitter:card", content: "summary_large_image" },
{
name: "twitter:site",
content:
settings &&
settings.meta &&
settings.meta.social &&
settings.meta.social.twitter,
},
{ name: "twitter:title", content: title },
{ name: "twitter:description", content: description },
{
name: "twitter:creator",
content:
settings &&
settings.meta &&
settings.meta.social &&
settings.meta.social.twitter,
},
{ name: "twitter:image:src", content: image },
{ name: "twitter:card", content: "summary_large_image" },
{ name: "og:title", content: title },
{ name: "og:type", content: openGraphType },
{ name: "og:url", content: url },
{ name: "og:image", content: image },
{ name: "og:description", content: description },
{
name: "og:site_name",
content: settings && settings.meta && settings.meta.title,
},
{
name: "og:published_time",
content: createdAt || new Date().toISOString(),
},
{
name: "og:modified_time",
content: updatedAt || new Date().toISOString(),
},
];
return metaTags;
};
const SEO = (props) => {
const { url, title, description, image } = props;
return (
<Head>
<title>{title} | App</title>
<meta name="description" content={description} />
<meta itemprop="name" content={title} />
<meta itemprop="description" content={description} />
<meta itemprop="image" content={image} />
{socialTags(props).map(({ name, content }) => {
return <meta key={name} name={name} content={content} />;
})}
</Head>
);
};
SEO.defaultProps = {
url: "/",
openGraphType: "website",
...
};
SEO.propTypes = {
url: PropTypes.string,
openGraphType: PropTypes.string,
...
};
export default SEO;
Antes de profundizar en las etiquetas sociales, muy rápido, queremos llamar la atención sobre el url
y openGraphType
campos que hemos agregado a nuestro propTypes
y defaultProps
. Estos representan el url
de la página en la que nos encontramos actualmente (por ejemplo, si estamos en una publicación de blog como /posts/the-slug-of-the-post
) y un openGraphType
que se asignará a un valor de tipo de la definición de tipos de objetos de Open Graph Protcol.
La parte que realmente Lo que importa aquí está en nuestro valor de retorno:el nuevo .map()
estamos haciendo.
Aquí, hemos introducido una función en la parte superior que devuelve una matriz de objetos con cada objeto que contiene el valor de un name
y content
atributo en un <meta />
etiqueta. Tenga en cuenta que los nombres cambian según la red social específica, pero la estructura no . Esto es intencional.
Mientras que Twitter y Facebook (el og
significa "Open Graph" aquí, que es un estándar creado por Facebook) tienen sus propios nombres de metadatos únicos, ambos usan el mismo mecanismo para compartir esos datos. En nuestro código, podemos aprovechar esto y recorrer una serie de objetos, cada uno de los cuales arroja un <meta />
etiqueta, pasando el name
y content
para el elemento actual que estamos recorriendo como atributos en la etiqueta.
Para realizar ese ciclo, llamamos al socialTags()
función primero, pasando el props
para nuestro componente y luego llene dinámicamente la matriz de objetos que la función devuelve con esos valores prop. A cambio, obtenemos una serie de objetos que anticipamos en nuestro return
valor.
Allí, encadenamos una llamada a .map()
en nuestra llamada a socialTags(props)
, y para cada elemento de la matriz devuelta, represente un <meta />
etiqueta con los atributos correspondientes para ese objeto.
Es importante tener en cuenta:lo que ves son solo algunos de las metaetiquetas disponibles para Twitter y Facebook. Dependiendo de su propio sitio, es posible que desee incluir menos o más etiquetas.
Para Twitter, puede hacer referencia a la documentación de marcado de su Tarjeta y para Facebook, hacer referencia a la documentación del protocolo Open Graph.
Con esto en su lugar, ahora, cuando nuestro contenido se comparte en Twitter o Facebook, obtendremos un elemento de "tarjeta" que se muestra correctamente y se ve bien en las líneas de tiempo de las personas.
Adición de metadatos JSON-LD de Google
Antes de poner nuestro <SEO />
componente a usar, queremos agregar un tipo más de metadatos:JSON-LD de Google (el "LD" significa "Datos de enlace"). Estos son los datos que utiliza Google para funciones como sus tarjetas de información en los resultados de búsqueda.
/componentes/SEO/index.js
import React from "react";
import PropTypes from "prop-types";
import Head from "next/head";
import settings from "../../settings";
const socialTags = ({
openGraphType,
url,
title,
description,
image,
createdAt,
updatedAt,
}) => { ... };
const SEO = (props) => {
const { url, title, description, image, schemaType } = props;
return (
<Head>
...
{socialTags(props).map(({ name, content }) => {
return <meta key={name} name={name} content={content} />;
})}
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify({
"@context": "http://schema.org",
"@type": schemaType,
name: title,
about: description,
url: url,
}),
}}
/>
</Head>
);
};
SEO.defaultProps = {
url: "/",
openGraphType: "website",
schemaType: "Article",
...
};
SEO.propTypes = {
url: PropTypes.string,
openGraphType: PropTypes.string,
schemaType: PropTypes.string,
...
};
export default SEO;
Justo debajo de nuestras etiquetas sociales .map()
, ahora, hemos agregado un <script />
etiqueta con un type
atributo establecido en application/ld+json
(el tipo MIME que busca Google cuando busca datos JSON-LD). Debido a que los datos para JSON-LD generalmente se pasan como un objeto entre las etiquetas del script, necesitamos "Reactificarlo" para que no tengamos ningún error de tiempo de ejecución.
Para hacerlo, aprovechamos el dangerouslySetInnerHTML
de React prop, pasándole un objeto con un __html
atributo establecido en la versión en cadena de nuestro objeto JSON-LD. Cuando esto se represente, React establecerá dinámicamente el objeto aquí como el HTML interno o contenido de nuestro <script />
etiqueta para nosotros (no hay diferencia para Google y funciona de todos modos).
En el objeto, hacemos uso de la estructura JSON-LD, utilizando las definiciones de tipo schema.org para describir nuestros datos.
¡Eso es todo! Para concluir, echemos un vistazo a cómo poner en uso nuestro componente.
Usando nuestro componente SEO
Para poner en uso nuestro componente, vamos a conectar rápidamente una página de ejemplo en nuestro modelo. Para hacerlo, vamos a crear una "publicación de blog" simulada en un archivo llamado /pages/post/index.js
:
/pages/post/index.js
import React from "react";
import PropTypes from "prop-types";
import SEO from "../../components/SEO";
import StyledPost from "./index.css";
const Post = (props) => (
<StyledPost>
<SEO
url={`${props.url}/post`}
openGraphType="website"
schemaType="article"
title="The Fate of Empires"
description="The only thing we learn from history, it has been said, 'is that men never learn from history'..."
image={`${props.url}/colosseum.jpeg`}
/>
<header>
<h1>The Fate of Empires</h1>
<h5>Sir John Glubb</h5>
</header>
<div>
<img src="/colosseum.jpeg" alt="Colosseum" />
<p>
As we pass through life, we learn by experience. We look back on our
behaviour when we were young and think how foolish we were. In the same
way our family, our community and our town endeavour to avoid the
mistakes made by our predecessors.
</p>
<p>
The experiences of the human race have been recorded, in more or less
detail, for some four thousand years. If we attempt to study such a
period of time in as many countries as possible, we seem to discover the
same patterns constantly repeated under widely differing conditions of
climate, culture and religion. Surely, we ask ourselves, if we studied
calmly and impartially the history of human institutions and development
over these four thousand years, should we not reach conclusions which
would assist to solve our problems today? For everything that is
occurring around us has happened again and again before.
</p>
<p>
No such conception ever appears to have entered into the minds of our
historians. In general, historical teaching in schools is limited to
this small island. We endlessly mull over the Tudors and the Stewarts,
the Battle of Crecy, and Guy Fawkes. Perhaps this narrowness is due to
our examination system, which necessitates the careful definition of a
syllabus which all children must observe.
</p>
<p>
The only thing we learn from history,’ it has been said, ‘is that men
never learn from history’, a sweeping generalisation perhaps, but one
which the chaos in the world today goes far to confirm. What then can be
the reason why, in a society which claims to probe every problem, the
bases of history are still so completely unknown?{" "}
</p>
</div>
</StyledPost>
);
Post.propTypes = {
url: PropTypes.string.isRequired,
};
export const getServerSideProps = (context) => {
return {
props: {
url: context?.req?.headers?.host,
},
};
};
export default Post;
La parte que más nos importa aquí es la representación de nuestro <SEO />
componente. Tenga en cuenta que hemos importado esto en la parte superior de nuestro archivo y lo estamos representando justo dentro del <StyledPost />
componente aquí (este es un tipo especial de componente React conocido como componente con estilo). Entonces lo tiene, muy rápido, aquí está la fuente de ese componente (preste atención a la ruta):
/pages/post/index.css.js
import styled from "styled-components";
export default styled.div`
max-width: 800px;
margin: 0 auto;
header {
margin: 25px 0;
padding: 0;
}
header h1 {
font-size: 28px;
font-weight: bold;
}
header h5 {
color: #888888;
}
div img {
max-width: 100%;
display: block;
margin: 0px 0px 25px;
}
div p {
font-size: 18px;
line-height: 28px;
}
@media screen and (min-width: 768px) {
header {
margin: 50px 0 50px;
padding: 0;
}
div img {
max-width: 100%;
display: block;
margin: 0px 0px 50px;
}
}
`;
Aquí, estamos usando el styled-components
biblioteca incluida en el modelo de Next.js que estamos usando para ayudarnos a crear dinámicamente un componente React que devuelve un HTML <div />
elemento con el CSS pasado entre los acentos graves aquí como los estilos for que <div />
. El qué y el por qué no son tan importantes para este tutorial, así que después de agregar este archivo, regresemos a nuestra página de publicaciones.
/pages/post/index.js
import React from "react";
import PropTypes from "prop-types";
import SEO from "../../components/SEO";
import StyledPost from "./index.css";
const Post = (props) => (
<StyledPost>
<SEO
url={`${props.url}/post`}
openGraphType="website"
schemaType="article"
title="The Fate of Empires"
description="The only thing we learn from history, it has been said, 'is that men never learn from history'..."
image={`${props.url}/colosseum.jpeg`}
/>
<header>
<h1>The Fate of Empires</h1>
<h5>Sir John Glubb</h5>
</header>
<div>
<img src="/colosseum.jpeg" alt="Colosseum" />
<p>
As we pass through life, we learn by experience. We look back on our
behaviour when we were young and think how foolish we were. In the same
way our family, our community and our town endeavour to avoid the
mistakes made by our predecessors.
</p>
<p>
The experiences of the human race have been recorded, in more or less
detail, for some four thousand years. If we attempt to study such a
period of time in as many countries as possible, we seem to discover the
same patterns constantly repeated under widely differing conditions of
climate, culture and religion. Surely, we ask ourselves, if we studied
calmly and impartially the history of human institutions and development
over these four thousand years, should we not reach conclusions which
would assist to solve our problems today? For everything that is
occurring around us has happened again and again before.
</p>
<p>
No such conception ever appears to have entered into the minds of our
historians. In general, historical teaching in schools is limited to
this small island. We endlessly mull over the Tudors and the Stewarts,
the Battle of Crecy, and Guy Fawkes. Perhaps this narrowness is due to
our examination system, which necessitates the careful definition of a
syllabus which all children must observe.
</p>
<p>
The only thing we learn from history,’ it has been said, ‘is that men
never learn from history’, a sweeping generalisation perhaps, but one
which the chaos in the world today goes far to confirm. What then can be
the reason why, in a society which claims to probe every problem, the
bases of history are still so completely unknown?{" "}
</p>
</div>
</StyledPost>
);
Post.propTypes = {
url: PropTypes.string.isRequired,
};
export const getServerSideProps = (context) => {
return {
props: {
url: context?.req?.headers?.host,
},
};
};
export default Post;
Mira nuestra representación del <SEO />
componente, como insinuamos durante su desarrollo, todo lo que estamos haciendo es pasar accesorios con los datos que queremos asignar a nuestras diversas metaetiquetas dentro del componente. Mientras codificamos nuestros accesorios de ejemplo aquí, técnicamente, podría (y probablemente lo hará) usar una expresión React para pasar algún valor variable dependiendo de dónde represente el componente.
Antes de que terminemos esto, muy rápido, queremos llamar la atención sobre el uso de getServerSideProps
cerca de la parte inferior de nuestro archivo. Esta es una función que utiliza Next.js para, como su nombre lo indica, obtener accesorios para nuestro componente en un contexto de servidor antes del lado del servidor representa nuestro componente. Esto es importante. La representación del lado del servidor es el término que se usa para describir la respuesta inicial enviada a una solicitud HTTP. Esa respuesta "representa" algo de HTML que recibe el solicitante.
Así funcionan los motores de búsqueda. Los sitios como Google tienen un "rastreador" que visita todas las URL públicas en Internet. Busca esta respuesta inicial para obtener el HTML que utiliza para generar resultados de búsqueda. Esto es precisamente cuando esperamos nuestro <SEO />
componente para ser procesado y "recogido" por los motores de búsqueda.
Aquí, dentro de getServerSideProps
queremos obtener la URL base (dominio actual) para la aplicación y pasarla a nuestro componente como accesorio url
. Queremos hacer esto para que cuando rendericemos nuestro <SEO />
componente como parte de la respuesta HTML inicial, la URL que pasamos para el url
prop en nuestro componente es correcto. Si no haga esto, la respuesta inicial que enviamos a un motor de búsqueda tendría una URL "indefinida".
Con eso, estamos listos para una prueba. Abramos el http://localhost:5000/post
página en nuestro navegador web y ver la fuente de nuestra página, comprobando para asegurarse de que nuestros metadatos se muestran como se esperaba:
Excelente. Debido a que vemos nuestros metadatos presentados aquí, podemos confiar en que esto es lo que verá Google (o cualquier otro motor de búsqueda) cuando su rastreador solicite nuestro sitio web.
Terminando
En este tutorial, aprendimos a conectar un <SEO />
personalizado Componente React para ayudarnos a representar dinámicamente las etiquetas de metadatos en función de los accesorios que le pasamos a ese componente. Aprendimos a representar el código HTML básico <meta />
etiquetas, así como las etiquetas necesarias para los sitios de redes sociales como Twitter y Facebook. Finalmente, aprendimos cómo agregar el JSON-LD <script />
de Google a nuestro componente para agregar más contexto y mejorar nuestras posibilidades de clasificación en los resultados de búsqueda.