{"id":28377,"date":"2022-04-19T08:25:41","date_gmt":"2022-04-19T08:25:41","guid":{"rendered":"http:\/\/make-an-internal-docs-system"},"modified":"2022-04-19T08:25:41","modified_gmt":"2022-04-19T08:25:41","slug":"make-an-internal-docs-system","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/","title":{"rendered":"Make an Internal Docs System"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Many companies face the issue of how to maintain internal documentation that connects all of the departments or products. Notion or other SaaS tools are the common answer to try and address this problem, but they usually miss some of the things you might want to customize internally.<\/p>\n<p>That\u2019s why we\u2019re going to make a Next.js app that will let us build our own internal docs system. We\u2019ll be able to upload screenshots or other images to Cloudinary and render Markdown on the page. You\u2019ll be able to expand this system to meet your own needs.<\/p>\n<h2>Setting up a few things<\/h2>\n<p>Before we jump into writing code, there are a few tools we need to have in place. First, we\u2019ll be using Cloudinary as the host for any images we need in our docs. So go <a href=\"https:\/\/cloudinary.com\/users\/register\/free\">make a free account here<\/a>.<\/p>\n<p>We\u2019ll be working with a PostgreSQL database to save the Markdown and any other data associated with a document. If you don\u2019t have a local instance, you can <a href=\"https:\/\/www.postgresql.org\/download\/\">download it for free here<\/a>. Make sure you note your username and password. You\u2019ll need that for the connection string to let your app interact with the database.<\/p>\n<h2>Generating the Next app<\/h2>\n<p>Now that we have the tools ready to use, let\u2019s create a new Next app by running the following command in your terminal.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ yarn create next-app --typescript\n<\/code><\/span><\/pre>\n<p>You will be prompted for a project name in the terminal. I\u2019ve creatively called this app <code>internal-docs<\/code>, but feel free to name it anything. This will generate a fully functional project that we can modify.<\/p>\n<p>There\u2019s one file we can go ahead and edit now. We know that we\u2019ll be working with Cloudinary for images and the Next app needs to know that it\u2019s ok to reference Cloudinary in the image sources. In the root of your project, open the <code>next.config.js<\/code> file and add the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ next.config.js<\/span>\n...\n  reactStrictMode: <span class=\"hljs-literal\">true<\/span>,\n  <span class=\"hljs-attr\">images<\/span>: {\n    <span class=\"hljs-attr\">domains<\/span>: &#91;<span class=\"hljs-string\">'res.cloudinary.com'<\/span>]\n  }\n...\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Now let\u2019s go ahead and install the packages that we\u2019ll be using with the following command.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ yarn add autoprefixer postcss tailwindcss axios prisma @prisma\/client @heroicons\/react\n<\/code><\/span><\/pre>\n<p>We\u2019ll be using Tailwind CSS to handle our styles so there\u2019s a little bit of setup we need to do for this package.<\/p>\n<h3>Initializing Tailwind CSS<\/h3>\n<p>With the packages installed, we can run the following CLI command to intialize Tailwind in our project.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ npx tailwindcss init -p\n<\/code><\/span><\/pre>\n<p>This will generate a couple of files that let our project use Tailwind. We need to edit the <code>tailwind.config.js<\/code> file so that it applies the styles to the files in our project. Open this file and add the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ tailwind.config.js<\/span>\n\n<span class=\"hljs-built_in\">module<\/span>.exports = {\n  <span class=\"hljs-attr\">content<\/span>: &#91;\n    <span class=\"hljs-string\">\".\/pages\/**\/*.{js,ts,jsx,tsx}\"<\/span>,\n    <span class=\"hljs-string\">\".\/components\/**\/*.{js,ts,jsx,tsx}\"<\/span>,\n  ],\n...\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Now we need to update the stylesheet in the project to use the Tailwind directives. Go to the <code>styles<\/code> directory and open the <code>global.css<\/code> file. You can delete all of the code out of this file and add the following.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-keyword\">@tailwind<\/span> base;\n<span class=\"hljs-keyword\">@tailwind<\/span> components;\n<span class=\"hljs-keyword\">@tailwind<\/span> utilities;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>These directives let us use all of the Tailwind functionality throughout the project because it\u2019s imported in the <code>_app.tsx<\/code> file, which is the top-level component in the project. That\u2019s all for our styles. Now we can set up the database schema.<\/p>\n<h2>Using Prisma for database operations<\/h2>\n<p>Most apps use some type of ORM that make it easier to interact with the database and we\u2019re going to use Prisma. This package was part of the ones we installed towards the beginning. Now we can initialize it in the project with this command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ npx prisma init\n<\/code><\/span><\/pre>\n<p>This will generate a <code>.env<\/code> file at the root of your project and a new <code>prisma<\/code> directory that has a <code>schema.prisma<\/code> file. The schema file connects the whole app to Postgres and is also where we will define the database schema. Let\u2019s start by editing the <code>.env<\/code> file and update the connection string with your local Postgres username, password, and database name. So that file should look similar to this.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\"># .env<\/span>\n\nDATABASE_URL=<span class=\"hljs-string\">\"postgresql:\/\/username:password@localhost:5432\/product_inventory\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Now we can dig into the <code>schema.prisma<\/code> file and start defining the schema that we\u2019ll eventually migrate to the database. So open that file and add the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ schema.prisma<\/span>\n\ngenerator client {\n  provider = <span class=\"hljs-string\">\"prisma-client-js\"<\/span>\n}\n\ndatasource db {\n  provider = <span class=\"hljs-string\">\"postgresql\"<\/span>\n  url      = env(<span class=\"hljs-string\">\"DATABASE_URL\"<\/span>)\n}\n\nmodel Document {\n  id      <span class=\"hljs-built_in\">String<\/span> @id @<span class=\"hljs-keyword\">default<\/span>(uuid())\n  title   <span class=\"hljs-built_in\">String<\/span>\n  content <span class=\"hljs-built_in\">String<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>The <code>Document<\/code> model includes all of the fields we need in the database. The <code>content<\/code> field is going to store all of our Markdown which will contain any images that are uploaded to Cloudinary. This is the only model we\u2019ll have to get this started, but feel free to add to this as you see fit.<\/p>\n<h3>Running the database migration<\/h3>\n<p>Now that the model is in place, we can run a migration to get this table schema into our database. In your terminal, run the following command.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ npx prisma migrate dev --name init\n<\/code><\/span><\/pre>\n<p>This will create a new database for you if you don\u2019t have it already and it will connect to the database and create a table called <code>Document<\/code> with the fields we defined. Now we can dive into the core functionality of the app and finally write some code.<\/p>\n<h2>Building CRUD functionality for documents<\/h2>\n<p>The base functionality we need for this system is the ability to look at all of the documents we have available, some way to add and edit docs, and a way to delete them. We\u2019ll start by creating the API routes that will handle all of this. Go to the <code>pages &gt; api<\/code> directory and delete the placeholder <code>hello.ts<\/code> file and add a new file called <code>new-doc.ts<\/code>.<\/p>\n<h3>Creating a new document<\/h3>\n<p>This will be the route we call to create a new document. Open this new file and add the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ new-doc.ts<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> type { NextApiRequest, NextApiResponse } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { PrismaClient } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@prisma\/client\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> type Document = {\n  <span class=\"hljs-attr\">title<\/span>: string;\n  content: string;\n};\n\n<span class=\"hljs-keyword\">const<\/span> prisma = <span class=\"hljs-keyword\">new<\/span> PrismaClient();\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handler<\/span>(<span class=\"hljs-params\">\n  req: NextApiRequest,\n  res: NextApiResponse&lt;Document&gt;\n<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> docData = req.body;\n  <span class=\"hljs-keyword\">const<\/span> newDoc = <span class=\"hljs-keyword\">await<\/span> prisma.document.create({\n    <span class=\"hljs-attr\">data<\/span>: {\n      <span class=\"hljs-attr\">title<\/span>: docData.title,\n      <span class=\"hljs-attr\">content<\/span>: docData.content,\n    },\n  });\n\n  res.status(<span class=\"hljs-number\">200<\/span>).json(newDoc);\n\n  <span class=\"hljs-keyword\">await<\/span> prisma.$disconnect();\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This code allows us to connect to Postgres using Prisma and then it creates a new record for the document. It takes the body from the request and uses that data to define the document. If you were working on a production-level app, you\u2019d need to include some validation around these user inputs to avoid potential security vulnerabilities.<\/p>\n<h3>Looking at existing documents<\/h3>\n<p>Let\u2019s add a new file called <code>docs.ts<\/code> in the <code>pages &gt; api<\/code> directory. This is how we will fetch all of the existing documents or a specific document by its ID. Now open your <code>docs.ts<\/code> file and add the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ docs.ts<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> type { NextApiRequest, NextApiResponse } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { PrismaClient } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@prisma\/client\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { Document } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/new-doc\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> prisma = <span class=\"hljs-keyword\">new<\/span> PrismaClient();\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handler<\/span>(<span class=\"hljs-params\">\n  req: NextApiRequest,\n  res: NextApiResponse&lt;Document | Document&#91;] | null&gt;\n<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> { id } = req.query;\n\n  <span class=\"hljs-keyword\">if<\/span> (id) {\n    <span class=\"hljs-keyword\">const<\/span> <span class=\"hljs-built_in\">document<\/span> = <span class=\"hljs-keyword\">await<\/span> prisma.document.findUnique({\n      <span class=\"hljs-attr\">where<\/span>: {\n        <span class=\"hljs-attr\">id<\/span>: id <span class=\"hljs-keyword\">as<\/span> string,\n      },\n    });\n\n    res.status(<span class=\"hljs-number\">200<\/span>).json(<span class=\"hljs-built_in\">document<\/span>);\n\n    <span class=\"hljs-keyword\">await<\/span> prisma.$disconnect();\n  } <span class=\"hljs-keyword\">else<\/span> {\n    <span class=\"hljs-keyword\">const<\/span> documents = <span class=\"hljs-keyword\">await<\/span> prisma.document.findMany();\n\n    res.status(<span class=\"hljs-number\">200<\/span>).json(documents);\n\n    <span class=\"hljs-keyword\">await<\/span> prisma.$disconnect();\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This code checks if there is an <code>id<\/code> query in the request URL. If there is one, this will query the database for a specific document. Without the <code>id<\/code> query, we return all of the documents from the database. Another thing for production to note is that you still need validation for the <code>id<\/code> value and possibly some pagination for returning all the docs.<\/p>\n<h3>Editing documents<\/h3>\n<p>Sometimes internal processes will change or big features will need a lot of research before work starts, so being able to update docs with new information is crucial. In the <code>pages &gt; api<\/code> directory, add a new file called <code>edit-doc.ts<\/code> and add the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ edit-doc.ts<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> type { NextApiRequest, NextApiResponse } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { PrismaClient } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@prisma\/client\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { Document } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/new-doc\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> prisma = <span class=\"hljs-keyword\">new<\/span> PrismaClient();\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handler<\/span>(<span class=\"hljs-params\">\n  req: NextApiRequest,\n  res: NextApiResponse&lt;Document&gt;\n<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> docData = req.body;\n\n  <span class=\"hljs-keyword\">const<\/span> updatedDoc = <span class=\"hljs-keyword\">await<\/span> prisma.document.update({\n    <span class=\"hljs-attr\">where<\/span>: { <span class=\"hljs-attr\">id<\/span>: docData.id },\n    <span class=\"hljs-attr\">data<\/span>: {\n      <span class=\"hljs-attr\">title<\/span>: docData.title,\n      <span class=\"hljs-attr\">content<\/span>: docData.content,\n    },\n  });\n\n  res.status(<span class=\"hljs-number\">200<\/span>).json(updatedDoc);\n\n  <span class=\"hljs-keyword\">await<\/span> prisma.$disconnect();\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here, the document ID is sent as part of the request body and we use it along with the updated data to the database to change the record. More production notes, you\u2019d want to record when these changes happened and who did them as well and of course, you can\u2019t forget the validation. You\u2019d also want some error handling just in case you run into issues updating a document.<\/p>\n<h3>Deleting a document<\/h3>\n<p>This can be a fun part of documentation maintenance. When you can go through and delete documentation, it usually means that things are moving forward and changing. So let\u2019s add one more file to the <code>pages &gt; api<\/code> called <code>delete-doc.ts<\/code> and add the following code to it.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ delete-doc.ts<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> type { NextApiRequest, NextApiResponse } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { PrismaClient } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@prisma\/client\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { Document } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/new-doc\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> prisma = <span class=\"hljs-keyword\">new<\/span> PrismaClient();\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handler<\/span>(<span class=\"hljs-params\">\n  req: NextApiRequest,\n  res: NextApiResponse&lt;Document&gt;\n<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> docId = req.body;\n\n  <span class=\"hljs-keyword\">const<\/span> deleteProduct = <span class=\"hljs-keyword\">await<\/span> prisma.document.delete({\n    <span class=\"hljs-attr\">where<\/span>: { <span class=\"hljs-attr\">id<\/span>: docId },\n  });\n\n  res.status(<span class=\"hljs-number\">200<\/span>).json(deleteProduct);\n\n  <span class=\"hljs-keyword\">await<\/span> prisma.$disconnect();\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This gets the document ID from the request body and runs the delete query in our database. An important thing to note is how we keep disconnecting from Prisma at the end of all of the responses. This makes sure we don\u2019t accidentally end up with 10 open Prisma clients.<\/p>\n<p>We just finished making all of the back-end CRUD functionality for this app, so it\u2019s time to build out the front-end that employees can interact with.<\/p>\n<h2>Displaying the documents<\/h2>\n<p>All of the docs will be displayed in a table and employees will be able to click on buttons in the rows to view, edit, or update an individual document. There will also be a button to allow employees to add new docs. Let\u2019s start by building the functionality for that button.<\/p>\n<h3>The new document button<\/h3>\n<p>Go to the <code>pages<\/code> directory and open the <code>index.tsx<\/code> file. Delete everything inside the <code>&lt;main&gt;<\/code> element. The first element we\u2019re going to add is the button to create a new document. Update your file to the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ index.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> type { NextPage } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> Head <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/head\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { PlusCircleIcon } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@heroicons\/react\/solid\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> Link <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/link\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> Home: NextPage = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Head<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span>Internal Docs<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">title<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"description\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"This is our internal docs system\"<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">link<\/span> <span class=\"hljs-attr\">rel<\/span>=<span class=\"hljs-string\">\"icon\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/favicon.ico\"<\/span> \/&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Head<\/span>&gt;<\/span>\n\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/doc\"<\/span> <span class=\"hljs-attr\">passHref<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex gap-2 bg-gray-700 hover:bg-gray-300 text-white font-bold py-2 px-4 rounded-full m-6\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PlusCircleIcon<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"h-6 w-6 text-green-500\"<\/span> \/&gt;<\/span>\n            Add new document\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> Home;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This button links to the new document page that will have the form you add your content to. It\u2019ll look like this in the browser if you run the app with <code>yarn dev<\/code>.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mediadevs\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1649949108\/e-603fc55d218a650069f5228b\/h5cztjwssz9pklhyfokz.png\" alt=\"add document button\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"532\"\/><\/p>\n<h3>New document page<\/h3>\n<p>Since we have the button linking to a page called <code>doc<\/code>, let\u2019s add some new things to our project directory. Create a new folder in the <code>pages<\/code> directory called <code>docs<\/code>. Inside this folder, add a new file called <code>index.tsx<\/code> and add the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ docs &gt; index.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> axios <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"axios\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> Link <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/link\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">NewDoc<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> submitDoc = <span class=\"hljs-keyword\">async<\/span> (e) =&gt; {\n    e.preventDefault();\n\n    <span class=\"hljs-keyword\">const<\/span> <span class=\"hljs-built_in\">document<\/span> = {\n      <span class=\"hljs-attr\">title<\/span>: e.target.title.value,\n      <span class=\"hljs-attr\">content<\/span>: e.target.content.value,\n    };\n\n    <span class=\"hljs-keyword\">await<\/span> axios.post(<span class=\"hljs-string\">\"\/api\/new-doc\"<\/span>, <span class=\"hljs-built_in\">document<\/span>);\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mt-4 ml-4\"<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-3xl mb-12\"<\/span>&gt;<\/span>New Document<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"w-full mb-12\"<\/span> <span class=\"hljs-attr\">onSubmit<\/span>=<span class=\"hljs-string\">{submitDoc}<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mb-6\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>\n            <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"block text-gray-500 font-bold mb-1 md:mb-0 pr-4\"<\/span>\n            <span class=\"hljs-attr\">htmlFor<\/span>=<span class=\"hljs-string\">\"title\"<\/span>\n          &gt;<\/span>\n            Title\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"md:w-2\/3\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n              <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"bg-gray-200 appearance-none border-2 border-gray-200 rounded w-full py-2 px-4 text-gray-700 leading-tight focus:outline-none focus:bg-white focus:border-blue-500\"<\/span>\n              <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"product-name\"<\/span>\n              <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"title\"<\/span>\n              <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span>\n            \/&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mb-6\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>\n              <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"block text-gray-500 font-bold mb-1 md:mb-0 pr-4\"<\/span>\n              <span class=\"hljs-attr\">htmlFor<\/span>=<span class=\"hljs-string\">\"content\"<\/span>\n            &gt;<\/span>\n              Content\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"md:w-2\/3\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">textarea<\/span>\n              <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"bg-gray-200 appearance-none border-2 border-gray-200 rounded block w-full py-2 px-4 text-gray-700 leading-tight focus:outline-none focus:bg-white focus:border-blue-500\"<\/span>\n              <span class=\"hljs-attr\">rows<\/span>=<span class=\"hljs-string\">{50}<\/span>\n              <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"content\"<\/span>\n              <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"content\"<\/span>\n            &gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">textarea<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"md:flex md:items-center gap-6\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"md:w-1\/3\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"shadow bg-blue-400 hover:bg-blue-500 focus:shadow-outline focus:outline-none text-white font-bold py-2 px-4 rounded\"<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/\"<\/span> <span class=\"hljs-attr\">passHref<\/span>&gt;<\/span>\n                Back\n              <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"md:w-2\/3\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span>\n              <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"shadow bg-blue-400 hover:bg-blue-500 focus:shadow-outline focus:outline-none text-white font-bold py-2 px-4 rounded\"<\/span>\n              <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span>\n            &gt;<\/span>\n              Save Document\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>On this page, we created a form that takes the title and content values for the new document and it calls the <code>new-doc<\/code> endpoint when an employee submits the form and it goes back to the main page when they cancel. A few more notes on production improvements, it would be great to have some kind of message show when an employee submits the form and have form validation on this page. Here\u2019s what the new document page looks like.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mediadevs\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1649948595\/e-603fc55d218a650069f5228b\/rauopj5ayqwkuwvvatte.png\" alt=\"new document form, buttons are below the content area\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"1328\"\/><\/p>\n<p>Now that we\u2019ve added a document, let\u2019s make the table for the main page to display all of the documents we have.<\/p>\n<h3>Displaying all the documents<\/h3>\n<p>To get all of the documents we have available, we\u2019re going to add some new functionality to the <code>pages &gt; index.tsx<\/code> file. Open this file and update the existing code to match the following code.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ pages &gt; index.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> Head <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/head\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> {\n  PencilAltIcon,\n  PlusCircleIcon,\n  TrashIcon,\n} <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@heroicons\/react\/solid\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> Link <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/link\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { Document } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/api\/new-doc\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> axios <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"axios\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> Home = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ documents }: { documents: Document&#91;] }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Head<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span>Internal Docs<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">title<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"description\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"This is our internal docs system\"<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">link<\/span> <span class=\"hljs-attr\">rel<\/span>=<span class=\"hljs-string\">\"icon\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/favicon.ico\"<\/span> \/&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Head<\/span>&gt;<\/span>\n\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/doc\"<\/span> <span class=\"hljs-attr\">passHref<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex gap-2 bg-gray-700 hover:bg-gray-300 text-white font-bold py-2 px-4 rounded-full m-6\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PlusCircleIcon<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"h-6 w-6 text-green-500\"<\/span> \/&gt;<\/span>\n            Add new document\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex flex-col gap-2\"<\/span>&gt;<\/span>\n          {documents.length &gt; 0 ? (\n            documents.map((doc: Document) =&gt; (\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{doc.id}<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex gap-6 border-b-2 w-full p-4\"<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>{doc.title}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">passHref<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">{<\/span>`\/<span class=\"hljs-attr\">doc<\/span>\/${<span class=\"hljs-attr\">doc.id<\/span>}`}&gt;<\/span>\n                  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PencilAltIcon<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"h-6 w-6 text-teal-500\"<\/span> \/&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">TrashIcon<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"h-6 w-6 text-rose-400\"<\/span> \/&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            ))\n          ) : (\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>Add some new products<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          )}\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">main<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getServerSideProps<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> docsRes = <span class=\"hljs-keyword\">await<\/span> axios.get(<span class=\"hljs-string\">\"http:\/\/localhost:3000\/api\/docs\"<\/span>);\n\n  <span class=\"hljs-keyword\">const<\/span> documents = <span class=\"hljs-keyword\">await<\/span> docsRes.data;\n\n  <span class=\"hljs-keyword\">return<\/span> {\n    <span class=\"hljs-attr\">props<\/span>: {\n      documents,\n    },\n  };\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> Home;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We\u2019ve added a few more imports to get new icons and the <code>axios<\/code> package. There\u2019s also a new call to the <code>getServerSideProps<\/code> method and that\u2019s where we call the back-end to get all of the documents we have in our database table. It returns the <code>documents<\/code> array as a prop to the <code>Home<\/code> component.<\/p>\n<p>After the new document button, we check the length of the array and show a list of documents or a message inviting users to make a new document. This is what the app will look like with the document we added earlier.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mediadevs\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1649963512\/e-603fc55d218a650069f5228b\/c1l3vygamsdv1hgq5laj.png\" alt=\"one document record in the table\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"494\"\/><\/p>\n<p>Now we can add the deletion functionality since we have that button on the document record and it\u2019ll be fast to implement.<\/p>\n<h3>Deleting a document<\/h3>\n<p>We need to add a function that calls the back-end with the correct document ID to delete. Add the <code>deleteDoc<\/code> function at the beginning of the <code>Home<\/code> component like below.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ pages &gt; index.tsx<\/span>\n\n...\nconst Home = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ documents }: { documents: Document&#91;] }<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">const<\/span> deleteDoc = <span class=\"hljs-keyword\">async<\/span> (docId: string) =&gt; {\n    <span class=\"hljs-keyword\">await<\/span> axios.delete(<span class=\"hljs-string\">\"\/api\/delete-doc\"<\/span>, { <span class=\"hljs-attr\">data<\/span>: docId });\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Head<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span>Internal Docs<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">title<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"description\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"This is our internal docs system\"<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">link<\/span> <span class=\"hljs-attr\">rel<\/span>=<span class=\"hljs-string\">\"icon\"<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/favicon.ico\"<\/span> \/&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Head<\/span>&gt;<\/span>\n...\n<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Then we\u2019ll call this function when someone clicks the trash icon on the row with the following code change.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/\/ pages &gt; index.tsx\n\n...\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">passHref<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">{<\/span>`\/<span class=\"hljs-attr\">doc<\/span>\/${<span class=\"hljs-attr\">doc.id<\/span>}`}&gt;<\/span>\n  <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PencilAltIcon<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"h-6 w-6 text-teal-500\"<\/span> \/&gt;<\/span>\n<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span>\n<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">TrashIcon<\/span>\n  <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"h-6 w-6 text-rose-400\"<\/span>\n  <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> deleteDoc(doc.id)}\n\/&gt;\n...\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>That\u2019s all we needed for the delete functionality! Let\u2019s finish up with the edit functionality.<\/p>\n<h3>Edit document page<\/h3>\n<p>We\u2019ll need to add a new page to the <code>pages &gt; doc<\/code> directory called <code>[id].tsx<\/code>. This page will show a form with the current document title and content and let employees edit the fields and save the changes. Add the following code to this new file.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ pages &gt; doc &gt; &#91;id].tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> axios <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"axios\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> Link <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/link\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { Document } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/api\/new-doc\"<\/span>;\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">EditDoc<\/span>(<span class=\"hljs-params\">{ doc }: { doc: Document }<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> submitDoc = <span class=\"hljs-keyword\">async<\/span> (e) =&gt; {\n    e.preventDefault();\n\n    <span class=\"hljs-keyword\">const<\/span> modifiedDoc = {\n      <span class=\"hljs-attr\">id<\/span>: doc.id,\n      <span class=\"hljs-attr\">title<\/span>: e.target.title.value,\n      <span class=\"hljs-attr\">content<\/span>: e.target.content.value,\n    };\n\n    <span class=\"hljs-keyword\">await<\/span> axios.patch(<span class=\"hljs-string\">\"\/api\/edit-doc\"<\/span>, modifiedDoc);\n  };\n\n  <span class=\"hljs-keyword\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mt-4 ml-4\"<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-3xl mb-12\"<\/span>&gt;<\/span>Edit {doc?.title || \"Document\"}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"w-full mb-12\"<\/span> <span class=\"hljs-attr\">onSubmit<\/span>=<span class=\"hljs-string\">{submitDoc}<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mb-6\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>\n            <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"block text-gray-500 font-bold mb-1 md:mb-0 pr-4\"<\/span>\n            <span class=\"hljs-attr\">htmlFor<\/span>=<span class=\"hljs-string\">\"title\"<\/span>\n          &gt;<\/span>\n            Title\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"md:w-2\/3\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n              <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"bg-gray-200 appearance-none border-2 border-gray-200 rounded w-full py-2 px-4 text-gray-700 leading-tight focus:outline-none focus:bg-white focus:border-blue-500\"<\/span>\n              <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"product-name\"<\/span>\n              <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"title\"<\/span>\n              <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span>\n              <span class=\"hljs-attr\">defaultValue<\/span>=<span class=\"hljs-string\">{doc<\/span> ? <span class=\"hljs-attr\">doc.title<\/span> <span class=\"hljs-attr\">:<\/span> \"\"}\n            \/&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mb-6\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span>\n              <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"block text-gray-500 font-bold mb-1 md:mb-0 pr-4\"<\/span>\n              <span class=\"hljs-attr\">htmlFor<\/span>=<span class=\"hljs-string\">\"content\"<\/span>\n            &gt;<\/span>\n              Content\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"md:w-2\/3\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">textarea<\/span>\n              <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"bg-gray-200 appearance-none border-2 border-gray-200 rounded block w-full py-2 px-4 text-gray-700 leading-tight focus:outline-none focus:bg-white focus:border-blue-500\"<\/span>\n              <span class=\"hljs-attr\">rows<\/span>=<span class=\"hljs-string\">{50}<\/span>\n              <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"content\"<\/span>\n              <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"content\"<\/span>\n              <span class=\"hljs-attr\">defaultValue<\/span>=<span class=\"hljs-string\">{doc<\/span> ? <span class=\"hljs-attr\">doc.content<\/span> <span class=\"hljs-attr\">:<\/span> \"\"}\n            &gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">textarea<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"md:flex md:items-center gap-6\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"md:w-1\/3\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"shadow bg-blue-400 hover:bg-blue-500 focus:shadow-outline focus:outline-none text-white font-bold py-2 px-4 rounded\"<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/\"<\/span> <span class=\"hljs-attr\">passHref<\/span>&gt;<\/span>\n                Back\n              <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"md:w-2\/3\"<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span>\n              <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"shadow bg-blue-400 hover:bg-blue-500 focus:shadow-outline focus:outline-none text-white font-bold py-2 px-4 rounded\"<\/span>\n              <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span>\n            &gt;<\/span>\n              Save Document\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n  );\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">getServerSideProps<\/span>(<span class=\"hljs-params\">context<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> docId = context.params.id;\n\n  <span class=\"hljs-keyword\">const<\/span> docRes = <span class=\"hljs-keyword\">await<\/span> axios.get(<span class=\"hljs-string\">\"http:\/\/localhost:3000\/api\/docs\"<\/span>, {\n    <span class=\"hljs-attr\">params<\/span>: {\n      <span class=\"hljs-attr\">id<\/span>: docId,\n    },\n  });\n\n  <span class=\"hljs-keyword\">const<\/span> doc = <span class=\"hljs-keyword\">await<\/span> docRes.data;\n\n  <span class=\"hljs-keyword\">return<\/span> {\n    <span class=\"hljs-attr\">props<\/span>: {\n      doc,\n    },\n  };\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This page starts off by calling the <code>getServerSideProps<\/code> method to fetch the data for the selected document based on the document ID passed in the URL and returns this as a prop to the <code>EditDoc<\/code> component.<\/p>\n<p>Then we render a form that uses the document data as the default values for the fields so that an employee can start making direct edits. There\u2019s also a function that will save any changes to the fields via a call to the back-end once the form is submitted.<\/p>\n<p>Just to keep up with the other sections, there are a few things you can do to improve this for production. Having some kind of authorization around who can make edits to certain documents could be useful. Adding a way to handle real-time team editing or locking a document until someone is finished editing can help prevent conflicts. Of course, form validation will always help with some security vulnerabilities.<\/p>\n<p>That\u2019s all! You now have an internal docs system template that you can expand in a number of directions.<\/p>\n<h2>Finished code<\/h2>\n<p>You can check out the complete code in the <a href=\"https:\/\/github.com\/flippedcoder\/media-projects\/tree\/main\/internal-docs\"><code>internal-docs<\/code> folder of this repo<\/a>. You can also check it out in <a href=\"https:\/\/codesandbox.io\/s\/restless-water-nyh02q\">this Code Sandbox<\/a>.<\/p>\n<p>&lt;CodeSandBox\ntitle=\u201crestless-water-nyh02q\u201d\nid=\u201crestless-water-nyh02q\u201d\n\/&gt;<\/p>\n<h2>Conclusion<\/h2>\n<p>There are a lot of different needs that your company or team might need for documentation and this gives you a simple starting point. You can make this as complex as you want or you can keep it as simple as you like. Either way, it\u2019ll be a fun way to start looking at documentation management.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":28378,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,212,246,371],"class_list":["post-28377","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-guest-post","tag-next-js","tag-react","tag-under-review"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Make an Internal Docs System<\/title>\n<meta name=\"description\" content=\"Managing company-wide docs can get messy quickly when everyone is using slightly different systems and standards to access and maintain everything. That&#039;s why we&#039;re going to make a small internal documentation managemenet system template with Next.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Make an Internal Docs System\" \/>\n<meta property=\"og:description\" content=\"Managing company-wide docs can get messy quickly when everyone is using slightly different systems and standards to access and maintain everything. That&#039;s why we&#039;re going to make a small internal documentation managemenet system template with Next.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-04-19T08:25:41+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924648\/Web_Assets\/blog\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9.jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2592\" \/>\n\t<meta property=\"og:image:height\" content=\"1728\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Make an Internal Docs System\",\"datePublished\":\"2022-04-19T08:25:41+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/\"},\"wordCount\":5,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924648\/Web_Assets\/blog\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9.jpg?_i=AA\",\"keywords\":[\"Guest Post\",\"Next.js\",\"React\",\"Under Review\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/\",\"name\":\"Make an Internal Docs System\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924648\/Web_Assets\/blog\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9.jpg?_i=AA\",\"datePublished\":\"2022-04-19T08:25:41+00:00\",\"description\":\"Managing company-wide docs can get messy quickly when everyone is using slightly different systems and standards to access and maintain everything. That's why we're going to make a small internal documentation managemenet system template with Next.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924648\/Web_Assets\/blog\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924648\/Web_Assets\/blog\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9.jpg?_i=AA\",\"width\":2592,\"height\":1728},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Make an Internal Docs System\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Make an Internal Docs System","description":"Managing company-wide docs can get messy quickly when everyone is using slightly different systems and standards to access and maintain everything. That's why we're going to make a small internal documentation managemenet system template with Next.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/","og_locale":"en_US","og_type":"article","og_title":"Make an Internal Docs System","og_description":"Managing company-wide docs can get messy quickly when everyone is using slightly different systems and standards to access and maintain everything. That's why we're going to make a small internal documentation managemenet system template with Next.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-04-19T08:25:41+00:00","og_image":[{"width":2592,"height":1728,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924648\/Web_Assets\/blog\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9.jpg?_i=AA","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/"},"author":{"name":"","@id":""},"headline":"Make an Internal Docs System","datePublished":"2022-04-19T08:25:41+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/"},"wordCount":5,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924648\/Web_Assets\/blog\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9.jpg?_i=AA","keywords":["Guest Post","Next.js","React","Under Review"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/","name":"Make an Internal Docs System","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924648\/Web_Assets\/blog\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9.jpg?_i=AA","datePublished":"2022-04-19T08:25:41+00:00","description":"Managing company-wide docs can get messy quickly when everyone is using slightly different systems and standards to access and maintain everything. That's why we're going to make a small internal documentation managemenet system template with Next.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924648\/Web_Assets\/blog\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924648\/Web_Assets\/blog\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9.jpg?_i=AA","width":2592,"height":1728},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/make-an-internal-docs-system\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Make an Internal Docs System"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924648\/Web_Assets\/blog\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9\/bd90568a1ef4c077adb9a39e73d1ce279095a062-2592x1728-1_28378ef2f9.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28377","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=28377"}],"version-history":[{"count":0,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28377\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/28378"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28377"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28377"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28377"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}