diff --git a/lua/leetcode-ui/group/page/problems.lua b/lua/leetcode-ui/group/page/problems.lua
index 8e0cfcd..589d1ca 100644
--- a/lua/leetcode-ui/group/page/problems.lua
+++ b/lua/leetcode-ui/group/page/problems.lua
@@ -21,6 +21,12 @@ local list = Button("List", {
     on_press = cmd.problems,
 })
 
+local recent = Button("Recent", {
+    icon = "",
+    sc = "i",
+    on_press = cmd.recent,
+})
+
 local random = Button("Random", {
     icon = "",
     sc = "r",
@@ -37,6 +43,7 @@ local back = BackButton("menu")
 
 page:insert(Buttons({
     list,
+    recent,
     random,
     daily,
     back,
diff --git a/lua/leetcode-ui/popup/console/result.lua b/lua/leetcode-ui/popup/console/result.lua
index 520113d..724ad4f 100644
--- a/lua/leetcode-ui/popup/console/result.lua
+++ b/lua/leetcode-ui/popup/console/result.lua
@@ -28,6 +28,9 @@ function ResultPopup:handle(item)
         end
     end
 
+    local Recent = require("leetcode.cache.recent")
+    Recent.add(self.console.question.q.title_slug)
+
     self:draw()
 end
 
diff --git a/lua/leetcode/cache/problemlist.lua b/lua/leetcode/cache/problemlist.lua
index 13cbf0d..0322978 100644
--- a/lua/leetcode/cache/problemlist.lua
+++ b/lua/leetcode/cache/problemlist.lua
@@ -32,6 +32,15 @@ function Problemlist.get()
     return Problemlist.read().data
 end
 
+---@return table<string, lc.cache.Question>
+function Problemlist.get_map()
+    local map = {}
+    for _, q in ipairs(Problemlist.get()) do
+        map[q.title_slug] = q
+    end
+    return map
+end
+
 ---@return lc.cache.payload
 function Problemlist.read()
     if not file:exists() then
diff --git a/lua/leetcode/cache/recent.lua b/lua/leetcode/cache/recent.lua
new file mode 100644
index 0000000..e51ee0c
--- /dev/null
+++ b/lua/leetcode/cache/recent.lua
@@ -0,0 +1,91 @@
+local Object = require("nui.object")
+local config = require("leetcode.config")
+local log = require("leetcode.logger")
+
+---@alias lc.recent table<string, integer>
+
+---@type Path
+local file = config.storage.cache:joinpath(("recent%s"):format(config.is_cn and "_cn" or ""))
+
+---@class lc.Recent
+local Recent = Object("LeetRecent")
+
+function Recent.populate()
+    Recent.write({})
+end
+
+---@param recent lc.recent|string
+function Recent.write(recent)
+    file:write("return " .. vim.inspect(recent), "w")
+end
+
+---@return lc.recent
+function Recent.get()
+    if not file:exists() then
+        Recent.populate()
+    end
+
+    local content = file:read()
+    local chunk = load(content)
+    return chunk()
+end
+
+---@return { slug: string, time: integer }[]
+function Recent.sorted()
+    local recent = Recent.get()
+
+    local sorted = {}
+    for slug, time in pairs(recent) do
+        table.insert(sorted, { slug = slug, time = time })
+    end
+
+    table.sort(sorted, function(a, b)
+        return a.time > b.time
+    end)
+
+    return sorted
+end
+
+function Recent.convert()
+    local map = require("leetcode.cache.problemlist").get_map()
+    local sorted = Recent.sorted()
+
+    local recent = {}
+    for _, item in ipairs(sorted) do
+        if map[item.slug] then
+            table.insert(recent, map[item.slug])
+        end
+    end
+    return recent
+end
+
+---@param slug string
+---@param recent? lc.recent
+function Recent.contains(slug, recent)
+    if not recent then
+        recent = Recent.get()
+    end
+
+    return recent[slug] == true
+end
+
+function Recent.add(slug)
+    local recent = Recent.get()
+
+    recent[slug] = os.time()
+    Recent.write(recent)
+
+    return true
+end
+
+function Recent.remove(slug)
+    local recent = Recent.get()
+
+    local tmp = recent[slug]
+    recent[slug] = nil
+
+    Recent.write(recent)
+    return tmp
+end
+
+return Recent
diff --git a/lua/leetcode/command/init.lua b/lua/leetcode/command/init.lua
index 1472b51..8ec3101 100644
--- a/lua/leetcode/command/init.lua
+++ b/lua/leetcode/command/init.lua
@@ -29,6 +29,13 @@ function cmd.problems(options)
     require("leetcode.pickers.question").pick(p, options)
 end
 
+function cmd.recent()
+    require("leetcode.utils").auth_guard()
+
+    -- local p = require("leetcode.cache.problemlist").get()
+    require("leetcode.pickers.recent").pick()
+end
+
 ---@param cb? function
 function cmd.cookie_prompt(cb)
     local cookie = require("leetcode.cache.cookie")
@@ -631,6 +638,7 @@ cmd.commands = {
         },
         update = { cmd.update_sessions },
     },
+    recent = { cmd.recent },
     list = {
         cmd.problems,
         _args = arguments.list,
diff --git a/lua/leetcode/pickers/recent.lua b/lua/leetcode/pickers/recent.lua
new file mode 100644
index 0000000..2585572
--- /dev/null
+++ b/lua/leetcode/pickers/recent.lua
@@ -0,0 +1,124 @@
+local log = require("leetcode.logger")
+local t = require("leetcode.translator")
+local utils = require("leetcode.utils")
+local ui_utils = require("leetcode-ui.utils")
+
+local Question = require("leetcode-ui.question")
+
+local pickers = require("telescope.pickers")
+local finders = require("telescope.finders")
+local conf = require("telescope.config").values
+local config = require("leetcode.config")
+
+local entry_display = require("telescope.pickers.entry_display")
+local actions = require("telescope.actions")
+local action_state = require("telescope.actions.state")
+
+---@param question lc.cache.Question
+---
+---@return string
+local function question_formatter(question)
+    return ("%s. %s %s %s"):format(
+        tostring(question.frontend_id),
+        question.title,
+        question.title_cn,
+        question.title_slug
+    )
+end
+
+---@param question lc.cache.Question
+local function display_difficulty(question)
+    local hl = ui_utils.diff_to_hl(question.difficulty)
+    return { config.icons.square, hl }
+end
+
+---@param question lc.cache.Question
+local function display_user_status(question)
+    if question.paid_only then
+        return config.auth.is_premium and config.icons.hl.unlock or config.icons.hl.lock
+    end
+
+    if question.status == vim.NIL then
+        return { "" }
+    end
+    return config.icons.hl.status[question.status] or { "" }
+end
+
+---@param question lc.cache.Question
+local function display_question(question)
+    local index = { question.frontend_id .. ".", "leetcode_normal" }
+    local title = { utils.translate(question.title, question.title_cn) }
+    return unpack({ index, title })
+end
+
+local displayer = entry_display.create({
+    separator = " ",
+    items = {
+        { width = 1 },
+        { width = 1 },
+        { width = 5 },
+        { remaining = true },
+    },
+})
+
+local function make_display(entry)
+    ---@type lc.cache.Question
+    local q = entry.value
+
+    return displayer({
+        display_user_status(q),
+        display_difficulty(q),
+        display_question(q),
+    })
+end
+
+local function entry_maker(entry)
+    return {
+        value = entry,
+        display = make_display,
+        ordinal = question_formatter(entry),
+    }
+end
+
+local theme = require("telescope.themes").get_dropdown({
+    layout_config = {
+        width = 100,
+        height = 20,
+    },
+})
+
+return {
+    pick = function()
+        local Recent = require("leetcode.cache.recent")
+        local questions = Recent.convert()
+
+        pickers
+            .new(theme, {
+                prompt_title = t("Recent Questions"),
+                finder = finders.new_table({
+                    results = questions,
+                    entry_maker = entry_maker,
+                }),
+                sorter = conf.generic_sorter(theme),
+                attach_mappings = function(prompt_bufnr, map)
+                    actions.select_default:replace(function()
+                        local selection = action_state.get_selected_entry()
+                        if not selection then
+                            return
+                        end
+
+                        local q = selection.value
+                        if q.paid_only and not config.auth.is_premium then
+                            return log.warn("Question is for premium users only")
+                        end
+
+                        actions.close(prompt_bufnr)
+                        Question(q):mount()
+                    end)
+
+                    return true
+                end,
+            })
+            :find()
+    end,
+}