{"id":13423784,"url":"https://github.com/elmasse/nextein","last_synced_at":"2025-03-15T17:32:17.100Z","repository":{"id":37546565,"uuid":"92770409","full_name":"elmasse/nextein","owner":"elmasse","description":"A static site generator with markdown + react for Next.js","archived":true,"fork":false,"pushed_at":"2023-05-31T14:23:31.000Z","size":2024,"stargazers_count":897,"open_issues_count":3,"forks_count":34,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-02-20T04:46:40.130Z","etag":null,"topics":["blog-engine","frontmatter","markdown","nextjs","react","static-site-generator"],"latest_commit_sha":null,"homepage":"https://nextein.elmasse.io","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elmasse.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"github":["elmasse"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":["https://cafecito.app/elmasse"]}},"created_at":"2017-05-29T19:46:23.000Z","updated_at":"2024-12-03T10:15:18.000Z","dependencies_parsed_at":"2024-01-22T05:49:37.993Z","dependency_job_id":null,"html_url":"https://github.com/elmasse/nextein","commit_stats":{"total_commits":622,"total_committers":11,"mean_commits":56.54545454545455,"dds":0.3585209003215434,"last_synced_commit":"0267cddbb9e962a35909248c93b2c46f0b3253ca"},"previous_names":[],"tags_count":92,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elmasse%2Fnextein","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elmasse%2Fnextein/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elmasse%2Fnextein/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elmasse%2Fnextein/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elmasse","download_url":"https://codeload.github.com/elmasse/nextein/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243767247,"owners_count":20344895,"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-engine","frontmatter","markdown","nextjs","react","static-site-generator"],"created_at":"2024-07-31T00:00:42.385Z","updated_at":"2025-03-15T17:32:15.842Z","avatar_url":"https://github.com/elmasse.png","language":"JavaScript","readme":"# nextein\n\nA static site generator based in Next.js\n\n![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/elmasse/nextein/node-ci.yml?branch=main)\n[![npm](https://img.shields.io/npm/v/nextein)](https://www.npmjs.com/package/nextein)\n\n#### [Site](https://nextein.elmasse.io/) | [Documentation](https://nextein.elmasse.io/docs) | [Guides](https://nextein.elmasse.io/guides)\n\n## What is it?\n`nextein` is  a wrapper around `next.js` that allows you to write static sites using `markdown` and `react`. \n\n### Requirements\n*NodeJS* __v10.x__+ is required to run `nextein` commands.\n\n## Starter Kit\nIf you want to jump into a starter project check [nextein-starter](https://github.com/elmasse/nextein-starter)\n\n## Getting Started\nThere are a few steps you have to follow to get your site up and running with `nextein`\n\n- Create a project:\n    -  `mkdir my-site`\n    -  `cd my-site`\n    -  `npm init -y` \n- Install Dependencies\n    -  `npm i nextein next react react-dom`\n- Add a `next.config.js` config file \n\n    ```js\n    const { withNextein } = require('nextein/config')\n\n    module.exports = withNextein({\n\n    })\n\n    ```\n- Create `pages/index.js`\n\n    ```js\n    import React from 'react'\n\n    import { getPosts } from 'nextein/fetcher'\n    import Content from 'nextein/content'\n\n    export async function getStaticProps () {\n      return {\n        props: {\n          posts: await getPosts()\n        }\n      }\n    }\n\n    export default function Index ({ posts }) {\n      return (\n        \u003csection\u003e\n        {\n          posts.map(post =\u003e \u003cContent {...post} /\u003e)\n        }\n        \u003c/section\u003e\n      )\n    })\n\n    ```\n- Create a `markdown` post entry under `posts` folder (`posts/my-first-post.md`)\n\n    ```md\n    ---\n    title: First Post\n    category: post\n    ---\n\n    This is the first paragraph and it will be used as an excerpt when loaded in a `\u003cContent excerpt /\u003e` tag.\n\n    This paragraph should *not* appear in that list.\n\n    ```\n- Add npm scripts to run dev mode to your `package.json`\n\n    ```json\n    \"scripts\": {\n      \"dev\": \"next\"\n    }\n    ```\n- Run the development server\n    - `npm run dev`\n    - open http://localhost:3000\n\n- Add another npm script to your `package.json` to export the site\n\n    ```json\n    \"scripts\": {\n      \"dev\": \"next\",\n      \"export\": \"next build \u0026\u0026 next export\"\n    }\n    ```\n\n## Documentation\n\n### `fetcher` \n\nUse fetcher to retrieve the posts and data from your markdown files. \n\nThe `getPostsFilterBy` and `getDataFilterBy` methods in fetcher allows to pass filter functions. For instance, we can use `inCategory` filter to retrieve posts in a given category:\n\n```js\nimport { getPostsFilterBy } from 'nextein/fetcher'\nimport { inCategory } from 'nextein/filters'\n\n//...\nconst blog = await getPostsFilterBy(InCategory('blog'))\n\n```\n\nThe `getData` and `getDataFilterBy` will retrieve only the metadata generated for entries instead of the entire post.\n\nThe `fetcher` method is a convenient way to define a filter and then use the `getPosts` and `getData` with a filter applied.\n\n```js\nimport fetcher from 'nextein/fetcher'\nimport { inCategory } from 'nextein/filters'\n\n//...\nconst { getPosts } = fetcher(InCategory('blog'))\n\n\n//...\nconst blog = await getPosts()\n\n```\n\n\n\nYou can use Dynamic Routes and *static generator functions* (getStaticProps and getStaticPaths) with fetcher methods.\n\nExample for a `[name].js` dynamic route\n\n```js\nimport fetcher from 'nextein/fetcher'\n\nconst { getData, getPost } = fetcher(/* filter */)\n\nexport async function getStaticPaths () {\n  const data = await getData()\n  return {\n    paths: data.map(({ name }) =\u003e ({ params: { name } })),\n    fallback: false\n  }\n}\n\nexport async function getStaticProps ({ params }) {\n  const post = await getPost(params)\n  return { props: { post } }\n}\n\nexport default function Post ({ post }) {\n  //...\n}\n\n```\n\nExample for a `[[...name]].js` dynamic route:\n\n```js\nimport fetcher from 'nextein/fetcher'\nimport { inCategory } from 'nextein/filters'\n\nconst { getData, getPosts, getPost } = fetcher(inCategory('guides'))\n\nexport async function getStaticPaths () {\n  const data = await getData()\n  return {\n    paths: [{ params: { name: [] } },\n      ...data.map(({ name }) =\u003e ({ params: { name: [name] } }))\n    ],\n    fallback: false\n  }\n}\n\nexport async function getStaticProps ({ params }) {\n  const posts = await getPosts()\n  const post = await getPost(params) // This can be null if not matching `...name`\n  return { props: { posts, post } }\n}\n\nexport default function Guides ({ posts, post }) {\n  //...\n}\n\n```\n\n### `inCategory(category, options)`\n\nFilter function to be applied to posts to retrieve posts in a given category.\n\n- `category`: `{String}` The category to filter results.\n- `options` : `{Object}` Optional\n    - `includeSubCategories:` `Boolean` true to include posts in sub categories. Default: `false`\n\nCategories are resolved by the folder structure by default. This means that a post located at `posts/categoryA/subOne` will have a category `categoryA/subOne` unless you specify the category name in frontmatter. \n\n```js\nimport { getPosts } from 'nextein/fetcher'\nimport { inCategory } from 'nextein/filters'\n\n//...\n\nconst posts = await getPosts()\nconst homePosts = posts.filter(inCategory('home'))\n  \n\n```\n\nIf you want to retrieve all posts under a certain category, let's say `categoryA` which will include all those under `subOne`, use the options `includeSubCategories: true`. \n\n```js\nimport { inCategory } from 'nextein/filters'\n\nconst categoryAPosts = posts\n  .filter(inCategory('categoryA', { includeSubCategories: true }))\n\n```\n\n### `Content`\n\nComponent to render a `post` object. This component receives the `content` from the post as a property.\nUse the `excerpt` property to only render the first paragraph (this is useful when rendering a list of posts).\n\n- `content`: `{Object}` Markdown content in HAST format to be render. This is provided by `post.content`\n- `excerpt`: `{Boolean}` true to only render the first paragraph. Optional. Default: `false`\n- `renderers`: `{Object}` A set of custom renderers for Markdown elements with the form of `[tagName]: renderer`.\n- `component`: `{String|React.Component}`\tThe component used for the root node.\n\n\n```js\n\nimport Content from 'nextein/content'\n\n//...\n\nexport default function PostPage ({ post }) {\n  return \u003cContent {...post} /\u003e\n} \n\n```\n\n\nUsing `renderers` to change/style the `\u003cp\u003e` tag\n\n```js\n\nconst Paragraph = ({ children }) =\u003e (\u003cp style={{padding:10, background: 'silver'}}\u003e { children } \u003c/p\u003e )\n\n// Then in your render method ...\n\n  \u003cContent\n    {...post} \n    renderers={{\n      p: Paragraph \n    }}\n  /\u003e\n\n```\n\n\n### `post`\n- `__id` is the unique identifier generated by nextein.\n- `data` is the frontmatter object containig the post meta information (title, page, category, etc)\n    - `data.category` is the post's category. When not specified, if the post is inside a folder, the directory structure under `posts` will be used. \n    - `data.date`: JSON date from frontmatter's date or date in file name or file creation date\n- `content` is representation of post content (generally in HAST format) created by the build plugin for a given mimeType.\n\n```js\n\n{ data, content } = post\n\n```\n\n### frontmatter\n\nThere are only a few defined properties in the frontmatter metadata that is used by `nextein`\n\n```md\n---\ncategory: categoryOne\ndate: 2017-06-23\n\n---\n\nPost Content...\n\n```\n\n- `category`: the category name (optional)\n- `date`: date string in YYYY-MM-DD format. Used to sort posts list. (optional)\n- `published`: Set to `false` to remove this post from entries.\n- `name`: **Read Only** The post file name. Date is removed from name if present.\n\n### `withNextein`\n\nA wrapper configuration function to be applied into the `next.config.js`. It provides a way to add your own `next.js` config along with `nextein` internal next.js config.\n\n\u003e next.config.js\n\n```js\nconst { withNextein } = require('nextein/config')\n\nmodule.exports = withNextein({\n  // Your own next.js config here\n})\n\n```\n\n## Plugins\n\nYou can also define nextein plugins using the `withNextein` configuration:\n\n```js\nconst { withNextein } = require('nextein/config')\n\nmodule.exports = withNextein({\n  nextein: {\n    plugins: [\n      //your nextein plugins here\n    ]\n  }\n  // Your own next.js config here\n})\n\n```\n\nThe `nextein.plugins` configuration accepts an array of plugins with the following formats:\n\n- ` [name] `: Just a string to define the plugin.\n- ` [name, options] `: A string to define the plugin and a plugins options object.\n- ` { name, id, options } `: A plugin object. The `name` field is required. All previous definitoins are transformed into this format. The `id` is optional, when provided allows multiple instances of the same plugin.\n\nThe plugin `name` should be a pre-installed plugin (`nextein-plugin-markdown`) , or a local file (`./myplugins/my-awesome-plugin`)\n\n### Default Plugins\n\nThe default configuration includes:\n\n```js\nplugins: [\n  ['nextein-plugin-source-fs', { path: 'posts', data: { page: 'post' } }],\n  'nextein-plugin-markdown',\n  'nextein-plugin-filter-unpublished'\n]\n\n```\n\n#### nextein-plugin-source-fs\n\nRead files from file system.\n\nOptions:\n\n- `path`: Path to read files from.\n- `data`: Default data to be passed as extra for each entry. Default to `{}`\n- `includes`: Default to `**/*.*`. \n- `ignore`: A set of ignored files. The default list includes:\n  ```js\n  '**/.DS_Store',\n  '**/.gitignore',\n  '**/.npmignore',\n  '**/.babelrc',\n  '**/node_modules',\n  '**/yarn.lock',\n  '**/package-lock.json'\n  ```\n\n#### nextein-plugin-markdown\n\nRender markdown files.\n\nOptions:\n\n- `raw`: Default to `true`. Make this `false` to not add the `raw` content in the post object.\n- `position`: Default to `false`. Make this `true` to add the position info to post content HAST.\n- `rehype`: Default to `[]`. Add a set of plugins for `rehype`\n- `remark`: Default to `[]`. Add a set of plugins for `remark`\n\n#### nextein-plugin-filter-unpublished\n\nFilter posts by using a property to prevent draft / unpublished entries to be displayed.\n\nOptions:\n\n- `field`: Default to`'published'`. Will check if a `field` is present in post `data` and filter if set to `false`.\n\n### Writing Plugins\n\nYou can write your own plugins. There are basically 2 different types (source and transforms). Source plugins will be called to generate the posts entries and then the transform plugins will receive those entries and can modify, filter, append, or transform in anyway the posts list.\n\nSee [plugins \u0026 lifecyle design document](./DESIGN.md).\n","funding_links":["https://github.com/sponsors/elmasse","https://cafecito.app/elmasse"],"categories":["Extensions","JavaScript","miscellaneous","Built on rehype"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felmasse%2Fnextein","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felmasse%2Fnextein","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felmasse%2Fnextein/lists"}