diff --git a/config.toml b/config.toml
index e36c23b2..26b722ac 100644
--- a/config.toml
+++ b/config.toml
@@ -28,3 +28,19 @@ unsafe = true
posts = '/posts/:year/:month/:day/:slug/'
[permalinks.section]
posts = '/posts'
+
+[outputs]
+home = ["HTML", "RSS", "Atom"]
+page = ["HTML"]
+section = ["HTML", "RSS", "Atom"]
+
+[mediaTypes."application/atom+xml"]
+suffixes = ["xml"]
+
+[outputFormats.Atom]
+mediaType = "application/atom+xml"
+baseName = "atom"
+isPlainText = false
+
+[services.rss]
+limit = 20
diff --git a/layouts/_default/atom.xml b/layouts/_default/atom.xml
new file mode 100644
index 00000000..a852debf
--- /dev/null
+++ b/layouts/_default/atom.xml
@@ -0,0 +1,53 @@
+{{- $pctx := . -}}
+{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
+{{- $pages := slice -}}
+{{- if or .IsHome .IsSection -}}
+{{- $pages = $pctx.RegularPages -}}
+{{- else -}}
+{{- $pages = $pctx.Pages -}}
+{{- end -}}
+{{- $limit := .Site.Config.Services.RSS.Limit -}}
+{{- if ge $limit 1 -}}
+{{- $pages = $pages | first $limit -}}
+{{- end -}}
+{{- printf "" | safeHTML }}
+
+ {{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}
+
+
+ {{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHTML }}
+ {{ .Permalink }}
+ {{- with .Site.Author.name }}
+
+ {{.}}
+ {{- with $.Site.Author.email }}
+ {{.}}
+ {{- end }}
+
+ {{- end }}
+ {{- with .Site.Copyright }}
+ {{ . }}
+ {{- end }}
+ {{ range $pages }}
+ {{- if and (ne .Layout "search") (ne .Layout "archives") }}
+
+ {{ .Title }}
+
+ {{ .Permalink }}
+ {{ .Lastmod.Format "2006-01-02T15:04:05-07:00" | safeHTML }}
+ {{ .Date.Format "2006-01-02T15:04:05-07:00" | safeHTML }}
+ {{- with .Site.Author.name }}
+
+ {{.}}
+
+ {{- end }}
+ {{ .Summary | html }}
+ {{- if .Params.preview_image }}
+ <img src="{{ .Site.BaseURL }}{{ .Params.preview_image }}" alt="{{ .Title }}" /><br/>{{ .Content | html }}
+ {{- else }}
+ {{ .Content | html }}
+ {{- end }}
+
+ {{- end }}
+ {{ end }}
+
diff --git a/layouts/_default/rss.xml b/layouts/_default/rss.xml
new file mode 100644
index 00000000..ecf289eb
--- /dev/null
+++ b/layouts/_default/rss.xml
@@ -0,0 +1,44 @@
+{{- $pctx := . -}}
+{{- if .IsHome -}}{{ $pctx = .Site }}{{- end -}}
+{{- $pages := slice -}}
+{{- if or .IsHome .IsSection -}}
+{{- $pages = $pctx.RegularPages -}}
+{{- else -}}
+{{- $pages = $pctx.Pages -}}
+{{- end -}}
+{{- $limit := .Site.Config.Services.RSS.Limit -}}
+{{- if ge $limit 1 -}}
+{{- $pages = $pages | first $limit -}}
+{{- end -}}
+{{- printf "" | safeHTML }}
+
+
+ {{ if eq .Title .Site.Title }}{{ .Site.Title }}{{ else }}{{ with .Title }}{{.}} on {{ end }}{{ .Site.Title }}{{ end }}
+ {{ .Permalink }}
+ Recent content {{ if ne .Title .Site.Title }}{{ with .Title }}in {{.}} {{ end }}{{ end }}on {{ .Site.Title }}
+ Hugo -- gohugo.io{{ with .Site.LanguageCode }}
+ {{.}}{{end}}{{ with .Site.Author.email }}
+ {{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}{{end}}{{ with .Site.Author.email }}
+ {{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}{{end}}{{ with .Site.Copyright }}
+ {{.}}{{end}}{{ if not .Date.IsZero }}
+ {{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}{{ end }}
+ {{- with .OutputFormats.Get "rss" -}}
+ {{ printf "" .Permalink .MediaType | safeHTML }}
+ {{- end -}}
+ {{ range $pages }}
+ {{- if and (ne .Layout "search") (ne .Layout "archives") }}
+ -
+ {{ .Title }}
+ {{ .Permalink }}
+ {{ .Date.Format "Mon, 02 Jan 2006 15:04:05 -0700" | safeHTML }}
+ {{ with .Site.Author.email }}{{.}}{{ with $.Site.Author.name }} ({{.}}){{end}}{{end}}
+ {{ .Permalink }}
+ {{ .Summary | html }}
+ {{- if .Params.preview_image }}
+
+ {{- end }}
+
+ {{- end }}
+ {{ end }}
+
+
diff --git a/next-app/api/atom/route.js b/next-app/api/atom/route.js
new file mode 100644
index 00000000..bcfcd6d7
--- /dev/null
+++ b/next-app/api/atom/route.js
@@ -0,0 +1,75 @@
+import { NextResponse } from 'next/server';
+import fs from 'fs';
+import path from 'path';
+import matter from 'gray-matter';
+
+const postsDirectory = path.join(process.cwd(), 'content/posts');
+
+function getAllPosts() {
+ const slugs = fs.readdirSync(postsDirectory);
+ const posts = slugs
+ .map((slug) => {
+ const fullPath = path.join(postsDirectory, slug);
+ const fileContents = fs.readFileSync(fullPath, 'utf8');
+ const { data, content } = matter(fileContents);
+
+ return {
+ slug: slug.replace(/\.md$/, ''),
+ frontmatter: data,
+ content,
+ fullPath,
+ };
+ })
+ .filter((post) => post.frontmatter.showInBlog !== false)
+ .sort((a, b) => new Date(b.frontmatter.date) - new Date(a.frontmatter.date));
+
+ return posts;
+}
+
+function generateAtomFeed(posts) {
+ const siteUrl = 'https://open-elements.com';
+ const feed = `
+
+ Open Elements
+
+
+ ${new Date().toISOString()}
+ ${siteUrl}/
+
+ Open Elements
+
+ ${posts.map(post => `
+
+ ${post.frontmatter.title}
+
+ ${siteUrl}/posts/${post.frontmatter.date ? post.frontmatter.date.substring(0, 4) : ''}/${post.frontmatter.date ? post.frontmatter.date.substring(5, 7) : ''}/${post.frontmatter.date ? post.frontmatter.date.substring(8, 10) : ''}/${post.slug}/
+ ${new Date(post.frontmatter.date || post.frontmatter.lastmod).toISOString()}
+ ${new Date(post.frontmatter.date).toISOString()}
+
+ ${post.frontmatter.author || 'Open Elements'}
+
+
+
` : ''}${post.content}]]>
+ `).join('')}
+`;
+
+ return feed;
+}
+
+export async function GET() {
+ try {
+ const posts = getAllPosts().slice(0, 20); // Limit to 20 most recent posts
+ const atomFeed = generateAtomFeed(posts);
+
+ return new NextResponse(atomFeed, {
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/atom+xml',
+ 'Cache-Control': 's-maxage=3600, stale-while-revalidate',
+ },
+ });
+ } catch (error) {
+ console.error('Error generating Atom feed:', error);
+ return new NextResponse('Error generating Atom feed', { status: 500 });
+ }
+}
diff --git a/next-app/api/rss/route.js b/next-app/api/rss/route.js
new file mode 100644
index 00000000..b442af5b
--- /dev/null
+++ b/next-app/api/rss/route.js
@@ -0,0 +1,72 @@
+import { NextResponse } from 'next/server';
+import fs from 'fs';
+import path from 'path';
+import matter from 'gray-matter';
+
+const postsDirectory = path.join(process.cwd(), 'content/posts');
+
+function getAllPosts() {
+ const slugs = fs.readdirSync(postsDirectory);
+ const posts = slugs
+ .map((slug) => {
+ const fullPath = path.join(postsDirectory, slug);
+ const fileContents = fs.readFileSync(fullPath, 'utf8');
+ const { data, content } = matter(fileContents);
+
+ return {
+ slug: slug.replace(/\.md$/, ''),
+ frontmatter: data,
+ content,
+ fullPath,
+ };
+ })
+ .filter((post) => post.frontmatter.showInBlog !== false)
+ .sort((a, b) => new Date(b.frontmatter.date) - new Date(a.frontmatter.date));
+
+ return posts;
+}
+
+function generateRSSFeed(posts) {
+ const siteUrl = 'https://open-elements.com';
+ const feed = `
+
+
+ Open Elements
+ ${siteUrl}
+ Open Source made right - Open Elements is a modern company with a clear focus on Open Source and Java
+ Next.js
+ en-us
+ ${new Date().toUTCString()}
+
+ ${posts.map(post => `
+ -
+ ${post.frontmatter.title}
+ ${siteUrl}/posts/${post.frontmatter.date ? post.frontmatter.date.substring(0, 4) : ''}/${post.frontmatter.date ? post.frontmatter.date.substring(5, 7) : ''}/${post.frontmatter.date ? post.frontmatter.date.substring(8, 10) : ''}/${post.slug}/
+ ${new Date(post.frontmatter.date).toUTCString()}
+ ${siteUrl}/posts/${post.frontmatter.date ? post.frontmatter.date.substring(0, 4) : ''}/${post.frontmatter.date ? post.frontmatter.date.substring(5, 7) : ''}/${post.frontmatter.date ? post.frontmatter.date.substring(8, 10) : ''}/${post.slug}/
+
+ ${post.frontmatter.preview_image ? `` : ''}
+
`).join('')}
+
+`;
+
+ return feed;
+}
+
+export async function GET() {
+ try {
+ const posts = getAllPosts().slice(0, 20); // Limit to 20 most recent posts
+ const rssFeed = generateRSSFeed(posts);
+
+ return new NextResponse(rssFeed, {
+ status: 200,
+ headers: {
+ 'Content-Type': 'application/rss+xml',
+ 'Cache-Control': 's-maxage=3600, stale-while-revalidate',
+ },
+ });
+ } catch (error) {
+ console.error('Error generating RSS feed:', error);
+ return new NextResponse('Error generating RSS feed', { status: 500 });
+ }
+}
diff --git a/next-app/package.json b/next-app/package.json
new file mode 100644
index 00000000..4745cf4d
--- /dev/null
+++ b/next-app/package.json
@@ -0,0 +1,25 @@
+{
+ "name": "open-elements-nextjs",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "next": "14.0.0",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "gray-matter": "^4.0.3"
+ },
+ "devDependencies": {
+ "@types/node": "^20",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "eslint": "^8",
+ "eslint-config-next": "14.0.0",
+ "typescript": "^5"
+ }
+}