Do you want to switch to Platzi in English?
7

Creando sitios estáticos con Next.js

20659Puntos

hace un año

Next.js nos permite crear aplicaciones de React fácilmente con server render y sin configuración. En su versión 3 (actualmente beta) incluyen una nueva característica y es poder crear sitios estáticos.

¿Qué es un sitio estático? Básicamente poder generar archivos .html en disco y que podamos luego llevar a producción fácilmente con Github Pages, Surge, Now, AWS S3, etc. Una ventaja de los sitios estáticos es que al ser un simple archivo en disco entrar a una página es super rápido, a comparación de un sitio dinámico que requiere hacer peticiones a un API o a una BD y luego generar el HTML dinámicamente con los datos obtenidos y a diferencia de una típica SPA no enviamos un HTML vacío, si no que el HTML que tenemos en disco ya tiene el contenido que necesitamos.

Iniciando el proyecto

Como siempre, vamos a iniciar nuestro proyecto y obtener un package.json ya sea que usen npm o yarn.

npm init --yes
# o con yarn
yarn init --yes

Instalando dependencias

Luego vamos a instalar las dependencias de nuestro proyecto, para eso vamos a correr uno de estos scripts.

npm i [email protected] react react-dom
# o con yarn
yarn add [email protected] react react-dom

Estamos usando una versión beta por lo que algunas cosas se pueden romper: si eso ocurre traten con otra versión beta inferior para ver si se arregla. Recuerden, usen las beta en producción bajo su propio riesgo.

Instalar dependencias

Una vez hecho esto vamos a crear la página de Next.js que vamos a exportar en nuestra aplicación. Para eso vamos a crear un archivo pages/index.js con este código.

import { Component } from'react'import Head from'next/head'exportdefaultclassextendsComponent{
	staticasync getInitialProps({ query }) {
		// vamos a cambiar el título dinámicamente dependiendo de un dato en la queryreturn { title: query.title || 'home page' }
	}
	render() {
		return (
			<main><Head><title>{this.props.title}</title></Head><header><h1>{this.props.title}</h1></header><p>
					Esta es nuestra página, el contenido es siempre el mismo, pero el título cambia.
				</p></main>
		)
	}
}

Esa es nuestra página. Como vemos vamos a obtener el title desde el querystring de la URL y vamos a modificar el título de la página tanto en la etiqueta <title /> como en la etiqueta <h1 /> con el valor de este. Por defecto mostramos el título home page si no está definido.

Iniciar servidor en desarrollo

Vamos ahora a probar que esto funcione en desarrollo, para eso simplemente vamos a definir estos scripts en el package.json.

{
	"scripts": {
		"dev": "next",
		"build": "next build",
		"start": "next start"
	}
}

Luego vamos a inicar nuestra aplicación con el siguiente script:

npm run dev
# o con yarn
yarn dev

Eso nos va a correr un servidor HTTP en el puerto 3000, si entramos entonces a localhost:3000 nos debe mostrar el siguiente HTML:

página sin title

Super simple, ahora si agregamos a nuestra URL el querystring ?title=hola%20mundo nos va a mostrar:

página con el title hola mundo

Como vemos nuestra pequeña página dinámica esta funcionando sin problemas. En el flujo normal de Next.js tendríamos que correr yarn build y luego yarn start para iniciar nuestra aplicación en producción, pero eso haría que nuestra página corra un servidor de Node.js, y no que genere archivos estáticos.

Configurando next export

Vamos entonces a configurar Next.js para que podamos exportar archivos HTML. Primero creemos un script nuevo.

{
	"scripts": {
		"dev": "next",
		"build": "next build",
		"export": "next export",
		"start": "next start"
	}
}

Ese nuevo script export nos permite decirle a Next.js que exporte nuestras páginas como HTML. Pero ya que nuestras páginas pueden ser, y en nuestro ejemplo son, dinámicas entonces necesitamos decirle como generar las posibles variaciones de títulos. Para eso vamos a crear un archivo llamado next.config.js en la raíz de nuestro proyecto con el siguiente contenido.

// usamos `module.exports` en vez de `export default` ya que este archivo// corre directo en Node.js y no pasa por Babelmodule.exports = {
	exportPathMap() {
		return {
			'/': { page: '/' },
		}
	}
}

Esa propiedad exportPathMap es quien le dice a Next que páginas exportar. La configuración es un simple objeto donde el nombre de la propiedad indica la URL que queremos manejar y el valor indica que página de Next.js (archivos dentro de pages) vamos a usar.

Haciendo el export

Ahora que tenemos una configuración básica vamos a hacer el export, para eso tenemos que correr el siguiente script en consola.

npm run build && npm run export# o con yarn
yarn build && yarn export

Primero hacemos el build de producción y luego hacemos export de las páginas que configuramos en next.config.js (por ahora una sola). Si vemos nuestro sistema de archivos obtuvimos una carpeta out con un archivo index.html y una carpeta _next con el código JS del lado del cliente (ya que la aplicación sigue funcionando del lado del cliente sin problemas y podemos actualizar el estado y ver el cambios en el navegador).

Probando en desarrollo

Vamos a probar nuestros HTML en desarrollo, para eso vamos a instalar serve un servidor HTTP super simple en Node.js que podemos iniciar por consola para archivos estáticos.

npm i serve
# o con yarn
yarn add serve

Luego agregamos el siguiente script al package.json.

{
	"scripts": {
		"dev": "next",
		"build": "next build",
		"export": "next export",
		"serve": "serve ./out",
		"start": "next start"
	}
}

Luego corremos nuestro servidor HTTP solo corriendo un comando:

npm run serve
# o con yarn
yarn serve

Eso nos inicia un servidor HTTP en el puerto 5000 y nos copia la URL al portapapeles, así que con solo ir al navegador y en un tab pegar la URL podemos acceder y ver nuestro HTML. Como podemos ver si entramos la carga es muy rápido y si recargamos ya que queda en cache cargar aún más rápido.

Páginas dinámicas

Recién exportamos una sola página, siempre la misma, pero nuestro pages/index.js recibe el título dinámicamente y podemos tener en realidad N páginas con diferentes títulos, vamos a hacer entonces que esto funcione.

Volvamos a nuestro next.config.js y agregamos una nueva página.

// usamos `module.exports` en vez de `export default` ya que este archivo// corre directo en Node.js y no pasa por Babelmodule.exports = {
	exportPathMap() {
		return {
			'/': { page: '/' },
			'/about': { page: '/', query: { title: 'About' }},
		}
	}
}

En el objeto que devuelve exportPathMap definimos una nueva propiedad /about, esta va a ser la nueva URL que vamos a exportar, pero esa URL va a usar el mismo archivo pages/index.js que usamos en /. La diferencia entonces es que agregamos una propiedad query cuyo valor es un objeto con todas las propiedades que queremos pasar como querystring.

Así al terminar de exportar la página vamos a obtener ahora una carpeta about/index.html dentro de out, si corremos nuestro servidor HTTP podemos entrar a /about y vamos a acceder a nuestro HTML donde el título va a ser About.

Pero todavía sigue siendo muy estático esto, si queremos exportar apartir de un dato obtenido de un API tendríamos que manejar todas las posible URL manualmente en nuestro exportPathMap. Para eso vamos a hacer instalar isomorphic-fetch y vamos a modificar nuestro next.config.js.

npm i isomorphic-fetch
# o con yarn
yarn add isomorphic-fetch

Luego en nuestro archivo de configuración:

// usamos `module.exports` en vez de `export default` ya que este archivo// corre directo en Node.js y no pasa por Babelconst fetch = require('isomorphic-fetch') // importamos fetchmodule.exports = {
	async exportPathMap() {
		const response = await fetch('http://jsonplaceholder.typicode.com/posts') // pedimos datos de un APIconst data = await response.json() // obtenemos los datos como JSON// convertimos el array de posts a un objeto, donde cada propiedad es la primer palabra// del título de una página y en la query colocamos el título enteroconst pages = data.reduce(
			(pages, page) => Object.assign(pages, {
				[`/${page.title.split(' ')[0]}`]: {
					page: '/',
					query: {
						title: page.title,
					},
				},
			}),
			{}
		)

		// luego combinamos esas páginas con nuestro home pagereturnObject.assign({
			'/': { page: '/' }
		}, pages)
	}
}

Si volvemos a hacer el export, vamos a ver que esta vez nos genera muchas páginas HTML, las cuales se generaron dinámicamente con la respuesta del API, si vemos en out vamos a encontrar todos los HTML que se generaron.

Deploy a producción

Ahora que tenemos nuestra página web con HTML generados dinámicamente en HTML nos toca llevar esto a producción, si usamos Now podemos hacer deploys que escalen infinitamente, gratis y super fácil. Así que para eso vamos a configurar el siguiente now.json.

{
	"name": "next-exported-site",
	"alias": "next-exported-site.now.sh",
	"type": "static"
}

Así definimos el nombre de nuestro deploy: el alias que vamos a usar, ya que Now nos genera URLs única con un ID que no están listas para producción y definimos que el tipo de deploy es estático.

No usen este mismo alias, ya que una vez usado un alias nadie más puede volverlo a usar excepto quien lo tomó, deben entonces usar uno diferente para ustedes.

Luego vamos a definir un script deploy en nuestro package.json.

{
	"scripts": {
		"dev": "next",
		"build": "next build",
		"export": "next export",
		"serve": "serve ./out",
		"start": "next start",
		"deploy": "now deploy out"
	}
}

Luego hacemo deploy con un simple script:

npm run deploy
# o con yarn
yarn deploy

Cuando hagamos deploy nos va a generar una URL similar a esta https://out-bpzolxbfsn.now.sh. Si vemos la URL dice out que es el nombre de la carpeta y un ID random. Sin embargo si siempre se llama out-ID sería molesto si tenemos varias aplicaciones, así que vamos a configurar Next.js para que la carpeta donde hagamos export no sea out.

Para eso vamos a nuestros scripts y modificamos el de export, serve y deploy.

{
	"scripts": {
		"dev": "next",
		"build": "next build",
		"export": "next export -o next-exported-site",
		"serve": "serve ./next-exported-site",
		"start": "next start",
		"deploy": "now deploy next-exported-site"
	}
}

Al agregar -o podemos configurar la carpeta de output donde Next.js va a colocar los HTML. Luego en nuestro script de deploy le decimos que en vez de out haga deploy de next-exported-site. Con eso volvemos a correr el deploy y esta vez obtenemos una URL similar a esta https://next-exported-site-mxsebrphfh.now.sh.

Ya que la página tiene muchos archivos HTML es posible que el deploy tarde un rato ya tiene que subir todos esos archivos HTML al servidor de Now.

Luego de que terminamos corremos el comando now alias que va a asignar el alias que definimos en now.json al último deploy que hagamos de next-exported-site. Si entran ya mismo a https://next-exported-site.now.sh/ van a ver el home page de nuestro sitio. Si entran a https://next-exported-site.now.sh/necessitatibus (por poner un ejemplo) vamos a poder acceder a otro HTML diferente.

Conclusiones

Como ven es súper simple usar Next.js para generar archivos HTML estáticos dinámicamente y luego llevarlos a producción. Podríamos generar una página para obtener la lista de posts de nuestro API en el getInitialProps y luego generar una lista de todos los posts con links a su contenido y así ir creando un sitio bastante complejo.

Luego gracias a que React sigue funcionando en el cliente podemos ir actualizando la UI si el usuario hace algo en el navegador.

Sergio Daniel
Sergio Daniel
@sergiodxa

20659Puntos

hace un año

Todas sus entradas
Escribe tu comentario
+ 2
Ordenar por:
1
277Puntos

Buen aporte!
Yo lo usaría para sitios webs informativos, por ejemplo con páginas como; inicio, about, galería, contacto.

Para qué casos se recomienda usar esa modalidad de páginas estáticas?

1
20659Puntos
un año

Podrías montar un blog en Github Pages por ejemplo, o landings, etc. También sirve para hacer apps de electron.js

0
3152Puntos

Tiene algun problema con el SEO!!? o es amigable al SEO!!?

1
20659Puntos
un año

Son archivos html normales, igual Next por defecto hace server render así que el seo nunca es un problema si usas Next.

0
1377Puntos

Como podria usar wow js con next js?