diff --git a/cmd/data/articles.go b/cmd/data/articles.go
index 6fdc370..0b0eb0d 100644
--- a/cmd/data/articles.go
+++ b/cmd/data/articles.go
@@ -9,7 +9,6 @@ type Article struct {
 	Created   time.Time
 	Desc      string
 	Content   string
-	Tags      []string
 	Published bool
 	ID        int64
 	AuthorID  int64
diff --git a/cmd/data/db.go b/cmd/data/db.go
index 7d5be05..5c548de 100644
--- a/cmd/data/db.go
+++ b/cmd/data/db.go
@@ -35,6 +35,18 @@ func OpenDB(dbName string) (*DB, error) {
 	return &db, nil
 }
 
+func (db *DB) UpdateAttribute(table string, id int64, attribute string, val interface{}) error {
+	query := `
+    UPDATE ?
+    SET ? = ?
+    WHERE id = ?
+    `
+	if _, err := db.Exec(query, table, attribute, val, id); err != nil {
+		return fmt.Errorf("error updating article in DB: %v", err)
+	}
+	return nil
+}
+
 func (db *DB) AddUser(user *User, pass string) error {
 	hashedPass, err := bcrypt.GenerateFromPassword([]byte(pass), bcrypt.DefaultCost)
 	if err != nil {
@@ -173,7 +185,7 @@ func (db *DB) GetUser(id int64) (*User, error) {
 }
 
 func (db *DB) AddTag(tagName string) error {
-	query := "INSERT INTO tags name VALUES ?"
+	query := "INSERT INTO tags (name) VALUES (?)"
 	if _, err := db.Exec(query, tagName); err != nil {
 		return fmt.Errorf("error inserting tag into DB: %v", err)
 	}
@@ -195,21 +207,31 @@ func (db *DB) GetTagList() ([]Tag, error) {
 		}
 		tagList = append(tagList, tag)
 	}
+	if err = rows.Err(); err != nil {
+		return nil, fmt.Errorf("error iterating through rows: %v", err)
+	}
 
 	return tagList, nil
 }
 
-func (db *DB) AddArticle(a *Article) error {
+func (db *DB) AddArticle(a *Article) (int64, error) {
 	query := `
     INSERT INTO articles
         (title, description, content, published, author_id)
     VALUES
-        (?, ?, ?, ?)
+        (?, ?, ?, ?, ?)
     `
-	if _, err := db.Exec(query, a.Title, a.Desc, a.Content, a.Published, a.AuthorID); err != nil {
-		return fmt.Errorf("error inserting article into DB: %v", err)
+
+	result, err := db.Exec(query, a.Title, a.Desc, a.Content, a.Published, a.AuthorID)
+	if err != nil {
+		return 0, fmt.Errorf("error inserting article into DB: %v", err)
 	}
-	return nil
+	id, err := result.LastInsertId()
+	if err != nil {
+		return 0, fmt.Errorf("error retrieving last ID: %v", err)
+	}
+
+	return id, nil
 }
 
 func (db *DB) GetArticle(id int64) (*Article, error) {
@@ -254,14 +276,27 @@ func (db *DB) GetUnpublishedArticles() ([]Article, error) {
 	return articleList, nil
 }
 
-func (db *DB) UpdateArticle(id int64, attribute string, val interface{}) error {
-	query := `
-    UPDATE articles
-    SET ? = ?
-    WHERE id = ?
-    `
-	if _, err := db.Exec(query, attribute, val, id); err != nil {
-		return fmt.Errorf("error updating article in DB: %v", err)
+func (db *DB) WriteArticleTags(articleID int64, tagIDs []int64) error {
+	tx, err := db.Begin()
+	if err != nil {
+		return fmt.Errorf("error starting transaction: %v", err)
+	}
+
+	for _, tagID := range tagIDs {
+		query := `
+        INSERT INTO articles_tags (article_id, tag_id)
+        VALUES (?, ?)
+        `
+		if _, err := tx.Exec(query, articleID, tagID); err != nil {
+			if rollbackErr := tx.Rollback(); rollbackErr != nil {
+				log.Fatalf("error: transaction error: %v, rollback error: %v", err, rollbackErr)
+			}
+			return fmt.Errorf("error inserting into articles_tags: %v", err)
+		}
+	}
+
+	if err = tx.Commit(); err != nil {
+		return fmt.Errorf("error committing transaction: %v", err)
 	}
 	return nil
 }
diff --git a/cmd/data/tags.go b/cmd/data/tags.go
index 8cb0fff..71361eb 100644
--- a/cmd/data/tags.go
+++ b/cmd/data/tags.go
@@ -1,6 +1,6 @@
 package data
 
 type Tag struct {
-	ID   int64
 	Name string
+	ID   int64
 }
diff --git a/cmd/main.go b/cmd/main.go
index b115de6..ac746a7 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -48,7 +48,8 @@ func main() {
 	store := data.NewCookieStore(key)
 
 	mux := http.NewServeMux()
-	mux.Handle("/web/static/", http.StripPrefix("/web/static/", http.FileServer(http.Dir("web/static/"))))
+	mux.Handle("/web/static/", http.StripPrefix("/web/static/",
+		http.FileServer(http.Dir("web/static/"))))
 	mux.HandleFunc("/", ui.HomePage(db, store))
 
 	mux.HandleFunc("GET /create-tag/", ui.CreateTag)
diff --git a/cmd/ui/articles.go b/cmd/ui/articles.go
index cb53e04..66551f9 100644
--- a/cmd/ui/articles.go
+++ b/cmd/ui/articles.go
@@ -65,9 +65,6 @@ func FinishArticle(db *data.DB, s *data.CookieStore) http.HandlerFunc {
 			return
 		}
 
-		r.ParseForm()
-		article.Tags = append(article.Tags, r.Form["tags"]...)
-
 		session, err := s.Get(r, "cookie")
 		if err != nil {
 			tmpl, err := template.ParseFiles("web/templates/login.html")
@@ -76,7 +73,29 @@ func FinishArticle(db *data.DB, s *data.CookieStore) http.HandlerFunc {
 		}
 
 		article.AuthorID = session.Values["id"].(int64)
-		db.AddArticle(article)
+		article.ID, err = db.AddArticle(article)
+		if err != nil {
+			log.Println(err)
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
+
+		r.ParseForm()
+		tags := make([]int64, 0)
+		for _, tag := range r.Form["tags"] {
+			tagID, err := strconv.ParseInt(tag, 10, 64)
+			if err != nil {
+				log.Println(err)
+				http.Error(w, err.Error(), http.StatusInternalServerError)
+				return
+			}
+			tags = append(tags, tagID)
+		}
+		if err = db.WriteArticleTags(article.ID, tags); err != nil {
+			log.Println(err)
+			http.Error(w, err.Error(), http.StatusInternalServerError)
+			return
+		}
 
 		tmpl, err := template.ParseFiles("web/templates/hub.html")
 		tmpl = template.Must(tmpl, err)
@@ -135,7 +154,7 @@ func PublishArticle(db *data.DB, c *data.Channel, s *data.CookieStore) http.Hand
 			return
 		}
 
-		if err = db.UpdateArticle(id, "published", true); err != nil {
+		if err = db.UpdateAttribute("articles", id, "published", true); err != nil {
 			log.Println(err)
 			http.Error(w, err.Error(), http.StatusInternalServerError)
 			return
@@ -160,7 +179,7 @@ func PublishArticle(db *data.DB, c *data.Channel, s *data.CookieStore) http.Hand
 			PubDate:     article.Created.Format(time.RFC1123Z),
 			Description: article.Desc,
 			Content:     &rss.Content{Value: article.Content},
-			Categories:  article.Tags,
+			// Categories:  article.Tags,
 		})
 		c.Save("tmp/rss.gob")
 
diff --git a/go.mod b/go.mod
index 184b8a6..e3b1fb3 100644
--- a/go.mod
+++ b/go.mod
@@ -5,8 +5,6 @@ go 1.22.0
 require (
 	git.streifling.com/jason/rss v0.0.0-20240305164907-524bf9676188
 	github.com/go-sql-driver/mysql v1.7.1
-	github.com/google/uuid v1.6.0
-	github.com/gorilla/feeds v1.1.2
 	github.com/gorilla/sessions v1.2.2
 	github.com/microcosm-cc/bluemonday v1.0.26
 	github.com/yuin/goldmark v1.7.0
diff --git a/go.sum b/go.sum
index 13ec948..e79d004 100644
--- a/go.sum
+++ b/go.sum
@@ -1,9 +1,3 @@
-git.streifling.com/jason/rss v0.0.0-20240305152729-9d8cc6464565 h1:/eO9NTksh+9yLz3HiYNR7BJo/iMTxxW5/d9h8I6vR6E=
-git.streifling.com/jason/rss v0.0.0-20240305152729-9d8cc6464565/go.mod h1:gpZF0nZbQSstMpyHD9DTAvlQEG7v4pjO5c7aIMWM4Jg=
-git.streifling.com/jason/rss v0.0.0-20240305160544-c8551159fe32 h1:G25NZzsD73rOkGYgV2vPUDviB0JXk5qi+dXOwB6J56U=
-git.streifling.com/jason/rss v0.0.0-20240305160544-c8551159fe32/go.mod h1:gpZF0nZbQSstMpyHD9DTAvlQEG7v4pjO5c7aIMWM4Jg=
-git.streifling.com/jason/rss v0.0.0-20240305160829-6cd08bb65d2a h1:TWQ9gwe7eWjaLUrZ0CJSc+sUUOw3VoGHlR3F8mH6vqs=
-git.streifling.com/jason/rss v0.0.0-20240305160829-6cd08bb65d2a/go.mod h1:gpZF0nZbQSstMpyHD9DTAvlQEG7v4pjO5c7aIMWM4Jg=
 git.streifling.com/jason/rss v0.0.0-20240305164907-524bf9676188 h1:C8M/j3f+cl5Y7YfGpU/ynb/SC/4tTYMDsyGFt3rswM8=
 git.streifling.com/jason/rss v0.0.0-20240305164907-524bf9676188/go.mod h1:gpZF0nZbQSstMpyHD9DTAvlQEG7v4pjO5c7aIMWM4Jg=
 github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
@@ -12,24 +6,14 @@ github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrt
 github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
 github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
 github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
-github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
 github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
-github.com/gorilla/feeds v1.1.2 h1:pxzZ5PD3RJdhFH2FsJJ4x6PqMqbgFk1+Vez4XWBW8Iw=
-github.com/gorilla/feeds v1.1.2/go.mod h1:WMib8uJP3BbY+X8Szd1rA5Pzhdfh+HCCAYT2z7Fza6Y=
 github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
 github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
 github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
 github.com/gorilla/sessions v1.2.2/go.mod h1:ePLdVu+jbEgHH+KWw8I1z2wqd0BAdAQh/8LRvBeoNcQ=
-github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
-github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
 github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
 github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
-github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
 github.com/yuin/goldmark v1.7.0 h1:EfOIvIMZIzHdB/R/zVrikYLPPwJlfMcNczJFMs1m6sA=
 github.com/yuin/goldmark v1.7.0/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
 golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
diff --git a/web/templates/editor.html b/web/templates/editor.html
index 303e8d6..6401141 100644
--- a/web/templates/editor.html
+++ b/web/templates/editor.html
@@ -5,8 +5,8 @@
     
     
     {{range .}}
-    
-    
+    
+    
     {{end}}