{"id":17545703,"url":"https://github.com/clxrityy/nextjs-md-blog","last_synced_at":"2026-04-12T13:05:29.684Z","repository":{"id":258565641,"uuid":"874175506","full_name":"clxrityy/nextjs-md-blog","owner":"clxrityy","description":"Nextjs markdown blog template with authentication, middleware, \u0026 prisma database","archived":false,"fork":false,"pushed_at":"2025-01-17T20:12:38.000Z","size":432,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-29T05:42:12.591Z","etag":null,"topics":["blog","github","markdown-editor","nextjs","postgres","prisma","tailwindcss","template","typescript","vercel"],"latest_commit_sha":null,"homepage":"https://nextjs-md-blog-sigma.vercel.app","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/clxrityy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-10-17T11:33:06.000Z","updated_at":"2025-01-17T20:12:34.000Z","dependencies_parsed_at":"2024-10-19T15:39:05.197Z","dependency_job_id":"30949ff1-20e2-4801-a456-71b417242de3","html_url":"https://github.com/clxrityy/nextjs-md-blog","commit_stats":null,"previous_names":["clxrityy/nextjs-md-blog"],"tags_count":0,"template":true,"template_full_name":null,"purl":"pkg:github/clxrityy/nextjs-md-blog","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clxrityy%2Fnextjs-md-blog","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clxrityy%2Fnextjs-md-blog/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clxrityy%2Fnextjs-md-blog/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clxrityy%2Fnextjs-md-blog/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/clxrityy","download_url":"https://codeload.github.com/clxrityy/nextjs-md-blog/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/clxrityy%2Fnextjs-md-blog/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265402173,"owners_count":23759098,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blog","github","markdown-editor","nextjs","postgres","prisma","tailwindcss","template","typescript","vercel"],"created_at":"2024-10-21T01:09:20.794Z","updated_at":"2025-12-30T20:06:41.419Z","avatar_url":"https://github.com/clxrityy.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Next.js Markdown Blog Template \u003cimg src=\"/logo.png\" alt=\"logo\" width=\"28px\" /\u003e\n\n![preview](https://i.gyazo.com/f73f986e554ae3275b9407c069cc6041.gif)\n\n## [🔗 Demo](https://nextjs-md-blog-sigma.vercel.app/)\n\n## Features\n\n- [Clerk authentication](#clerk-authentication)\n    - [Middleware](#middleware)\n- [Prisma database](#prisma)\n- [Markdown editor](#markdown-editor)\n\n\u003cdetails\u003e\n    \u003csummary\u003e\u003cb\u003ePreview\u003c/b\u003e\u003c/summary\u003e\n\n#### Before Authentication\n![before auth](https://i.gyazo.com/e8789ac6ab6629a6b366e8886a841020.png)\n\n---\n\n#### After Authentication\n![after auth](https://i.gyazo.com/f1db171173d96e0b81ac39e20c59d295.png)\n\u003c/details\u003e\n\n## Set up\n\n##### Clone or use the template\n```zsh\ngit clone https://github.com/clxrityy/nextjs-md-blog.git\n```\n##### Include your [`.env`](/.env.example) variables\n```env\nPOSTGRES_URL=\"\"\nPOSTGRES_URL_NON_POOLING=\"\"\nNEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=\"\"\nCLERK_SECRET_KEY=\"\"\n```\n\n- Acquire your `POSTGRES_URL` \u0026 `POSTGRES_URL_NON_POOLING` through vercel\n    - See [this guide](https://vercel.com/docs/storage/vercel-postgres) on Postgres with Vercel\n- Get your `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY` \u0026 `CLERK_SECRET_KEY` on your [clerk dashboard](https://dashboard.clerk.com/)\n\n---\n\n## Clerk Authentication\n\nClerk is a very simple authentication tool. \n\nRather than going through step by step instructions, give the [Clerk Next.js Quickstart](https://clerk.com/docs/quickstarts/nextjs) guide a look for more information.\n\n### Middleware\n\nTwo vital imports are required for the middleware to work \u0026 protect the appropriate routes:\n\n- [`clerkMiddleware()`](https://clerk.com/docs/references/nextjs/clerk-middleware)\n- [`createRouteMatcher()`](https://clerk.com/docs/references/nextjs/clerk-middleware#create-route-matcher)\n\n```ts\nimport { clerkMiddleware, createRouteMatcher } from \"@clerk/nextjs/server\";\n```\n\n- Per all Next.js middleware, export the [config matcher](https://nextjs.org/docs/app/building-your-application/routing/middleware#matcher) to determine what files/routes the middleware function will apply to:\n    ```ts\n    export const config = {\n        matcher: [\n            // Skip Next.js internals and all static files, unless found in search params\n            '/((?!_next|[^?]*\\\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',\n            // Always run for API routes\n            '/(api|trpc)(.*)',\n        ]\n    }\n    ```\n- Create a *route matcher* that declares which routes to protect\n    ```ts\n    const isAuthenticatedRoute = createRouteMatcher([\n        \"/blog/new\", // Ensure no unauthenticated users can post a blog\n    ])\n    ```\n- Export the clerk middleware (must be exported as **default**) and handle the authenticated route(s)\n    ```ts\n    export default clerkMiddleware((auth, req) =\u003e {\n        const { \n            sessionClaims // auth object properties\n        } = auth();\n\n        if (isAuthenticatedRoute(req) \u0026\u0026 !sessionClaims) {\n            /**\n             * If the request is an authenticated route and there's no auth object properties, redirect to sign in\n             */\n            auth().redirectToSignIn();\n        }\n    })\n    ```\n\n---\n\n## Prisma\n\nIf you're deploying with [Vercel](https://vercel.com/) (recommended): on your dashboard under the **Storage** tab, there is an option to set up a **free** postgres database (regardless of your plan).\n\nOnce you've set that up, it should be pretty straight forward to get your `.env` variables to get started with Prisma.\n\n![vercel postgres prisma quickstart](https://i.gyazo.com/b5af554da042dae4cf5cf2aa2739fea3.png)\n\nPrisma should already be installed if you're using this repo; but if not:\n```zsh\npnpm add @prisma/client\n```\n```zsh\npnpm add --save-dev prisma\n```\n\n### Setting Up Prisma\n\n1. Prisma init\n    ```zsh\n    npx prisma init\n    ```\n    - This will create [`/prisma/schema.prisma`](/prisma/schema.prisma) within the root of your application.\n2. Ensure your [generator/client](https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/generating-prisma-client) \u0026 [datasource](https://www.prisma.io/docs/orm/prisma-schema/overview/data-sources) are directing to the proper values. In this case:\n    ```prisma\n    generator client {\n        provider = \"prisma-client-js\"\n    }\n\n    datasource db {\n        provider  = \"postgresql\"\n        url       = env(\"POSTGRES_URL\")\n        directUrl = env(\"POSTGRES_URL_NON_POOLING\")\n    }\n    ```\n3. Generate the prisma client\n    ```zsh\n    npx prisma generate\n    ```\n4. Define your prisma client\n    ```ts\n    /**\n     * ~/lib/db.ts\n     */\n    import { PrismaClient } from \"@prisma/client\";\n    // ...\n    ```\n5. With any changes to models within your schema, ensure you're pushing and pulling your changes locally:\n    ```zsh\n    npx prisma db push\n    ```\n    ```zsh\n    npx prisma db pull\n    ```\n6. Lastly, update your `build` script within your `package.json` to generate your prisma client every time the application is built\n    - (Update this script on vercel for production as well)\n    ```json\n    \"scripts\": {\n        \"build\": \"prisma generate \u0026\u0026 next build\"\n    }\n    ```\n---\n\n## Markdown Editor\n\nThe markdown editor comes from the package [`react-mde`](https://github.com/andrerpena/react-mde).\n\n```tsx\n\"use client\";\n\nimport { ComponentPropsWithoutRef } from \"react\";\nimport ReactMde from \"react-mde\";\nimport \"react-mde/lib/styles/css/react-mde-all.css\";\nimport \"@/styles/mde.css\";\n\ntype EditorProps = ComponentPropsWithoutRef\u003ctypeof ReactMde\u003e;\n\nconst Editor = ({ ...props }: EditorProps) =\u003e {\n    return (\n        \u003cReactMde\n            value={props.value}\n            onChange={props.onChange}\n            selectedTab={props.selectedTab}\n            onTabChange={props.onTabChange}\n            generateMarkdownPreview={props.generateMarkdownPreview}\n            maxEditorHeight={props.maxEditorHeight}\n            minEditorHeight={props.minEditorHeight}\n        /\u003e\n    )\n}\n\nexport default Editor;\n```\n\n---\n\n## Issues \u0026 Contributions\n\n- Make a [pull request](https://github.com/clxrityy/nextjs-md-blog/pulls)\n- Report an [issue](https://github.com/clxrityy/nextjs-md-blog/issues)\n- Contact me at [contact@mjanglin.com](mailto:contact@mjanglin.com)\n\n\u003e Thank you \u003c3","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclxrityy%2Fnextjs-md-blog","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclxrityy%2Fnextjs-md-blog","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclxrityy%2Fnextjs-md-blog/lists"}