diff --git a/README.md b/README.md
index 5465737..d3c1461 100644
--- a/README.md
+++ b/README.md
@@ -93,7 +93,7 @@ require("lazy").setup({
 | **dependencies** | `LazySpec[]`                                                                                                                        | A list of plugin names or plugin specs that should be loaded when the plugin loads. Dependencies are always lazy-loaded unless specified otherwise. When specifying a name, make sure the plugin spec has been defined somewhere else.                                                                                                                                                                                 |
 | **init**         | `fun(LazyPlugin)`                                                                                                                   | `init` functions are always executed during startup                                                                                                                                                                                                                                                                                                                                                                    |
 | **opts**         | `table` or `fun(LazyPlugin, opts:table)`                                                                                            | `opts` should be a table (will be merged with parent specs), return a table (replaces parent specs) or should change a table. The table will be passed to the `Plugin.config()` function. Setting this value will imply `Plugin.config()`                                                                                                                                                                              |
-| **config**       | `fun(LazyPlugin, opts:table)` or `true`                                                                                             | `config` is executed when the plugin loads. The default implementation will automatically run `require(MAIN).setup(opts)` if `opts` or `config = true` is set. Lazy uses several heuristics to determine the plugin's `MAIN` module automatically based on the plugin's **name**. See also `opts`. To use the default implementation without `opts` set `config` to `true`.                                                                                |
+| **config**       | `fun(LazyPlugin, opts:table)` or `true`                                                                                             | `config` is executed when the plugin loads. The default implementation will automatically run `require(MAIN).setup(opts)` if `opts` or `config = true` is set. Lazy uses several heuristics to determine the plugin's `MAIN` module automatically based on the plugin's **name**. See also `opts`. To use the default implementation without `opts` set `config` to `true`.                                            |
 | **main**         | `string?`                                                                                                                           | You can specify the `main` module to use for `config()` and `opts()`, in case it can not be determined automatically. See `config()`                                                                                                                                                                                                                                                                                   |
 | **build**        | `fun(LazyPlugin)` or `string` or a list of build commands                                                                           | `build` is executed when a plugin is installed or updated. Before running `build`, a plugin is first loaded. If it's a string it will be ran as a shell command. When prefixed with `:` it is a Neovim command. You can also specify a list to executed multiple build commands. Some plugins provide their own `build.lua` which is automatically used by lazy. So no need to specify a build step for those plugins. |
 | **branch**       | `string?`                                                                                                                           | Branch of the repository                                                                                                                                                                                                                                                                                                                                                                                               |
@@ -307,6 +307,7 @@ return {
   },
   -- leave nil when passing the spec as the first argument to setup()
   spec = nil, ---@type LazySpec
+  local_spec = true, -- load project specific .lazy.lua spec files. They will be added at the end of the spec.
   lockfile = vim.fn.stdpath("config") .. "/lazy-lock.json", -- lockfile generated after running update.
   ---@type number? limit the maximum amount of concurrent tasks
   concurrency = jit.os:find("Windows") and (vim.uv.available_parallelism() * 2) or nil,
@@ -516,24 +517,24 @@ Any operation can be started from the UI, with a sub command or an API function:
 
 <!-- commands:start -->
 
-| Command | Lua | Description |
-| --- | --- | --- |
-| `:Lazy build {plugins}` | `require("lazy").build(opts)` | Rebuild a plugin |
-| `:Lazy check [plugins]` | `require("lazy").check(opts?)` | Check for updates and show the log (git fetch) |
-| `:Lazy clean [plugins]` | `require("lazy").clean(opts?)` | Clean plugins that are no longer needed |
-| `:Lazy clear` | `require("lazy").clear()` | Clear finished tasks |
-| `:Lazy debug` | `require("lazy").debug()` | Show debug information |
-| `:Lazy health` | `require("lazy").health()` | Run `:checkhealth lazy` |
-| `:Lazy help` | `require("lazy").help()` | Toggle this help page |
-| `:Lazy home` | `require("lazy").home()` | Go back to plugin list |
-| `:Lazy install [plugins]` | `require("lazy").install(opts?)` | Install missing plugins |
-| `:Lazy load {plugins}` | `require("lazy").load(opts)` | Load a plugin that has not been loaded yet. Similar to `:packadd`. Like `:Lazy load foo.nvim`. Use `:Lazy! load` to skip `cond` checks. |
-| `:Lazy log [plugins]` | `require("lazy").log(opts?)` | Show recent updates |
-| `:Lazy profile` | `require("lazy").profile()` | Show detailed profiling |
-| `:Lazy reload {plugins}` | `require("lazy").reload(opts)` | Reload a plugin (experimental!!) |
+| Command                   | Lua                              | Description                                                                                                                                          |
+| ------------------------- | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
+| `:Lazy build {plugins}`   | `require("lazy").build(opts)`    | Rebuild a plugin                                                                                                                                     |
+| `:Lazy check [plugins]`   | `require("lazy").check(opts?)`   | Check for updates and show the log (git fetch)                                                                                                       |
+| `:Lazy clean [plugins]`   | `require("lazy").clean(opts?)`   | Clean plugins that are no longer needed                                                                                                              |
+| `:Lazy clear`             | `require("lazy").clear()`        | Clear finished tasks                                                                                                                                 |
+| `:Lazy debug`             | `require("lazy").debug()`        | Show debug information                                                                                                                               |
+| `:Lazy health`            | `require("lazy").health()`       | Run `:checkhealth lazy`                                                                                                                              |
+| `:Lazy help`              | `require("lazy").help()`         | Toggle this help page                                                                                                                                |
+| `:Lazy home`              | `require("lazy").home()`         | Go back to plugin list                                                                                                                               |
+| `:Lazy install [plugins]` | `require("lazy").install(opts?)` | Install missing plugins                                                                                                                              |
+| `:Lazy load {plugins}`    | `require("lazy").load(opts)`     | Load a plugin that has not been loaded yet. Similar to `:packadd`. Like `:Lazy load foo.nvim`. Use `:Lazy! load` to skip `cond` checks.              |
+| `:Lazy log [plugins]`     | `require("lazy").log(opts?)`     | Show recent updates                                                                                                                                  |
+| `:Lazy profile`           | `require("lazy").profile()`      | Show detailed profiling                                                                                                                              |
+| `:Lazy reload {plugins}`  | `require("lazy").reload(opts)`   | Reload a plugin (experimental!!)                                                                                                                     |
 | `:Lazy restore [plugins]` | `require("lazy").restore(opts?)` | Updates all plugins to the state in the lockfile. For a single plugin: restore it to the state in the lockfile or to a given commit under the cursor |
-| `:Lazy sync [plugins]` | `require("lazy").sync(opts?)` | Run install, clean and update |
-| `:Lazy update [plugins]` | `require("lazy").update(opts?)` | Update plugins. This will also update the lockfile |
+| `:Lazy sync [plugins]`    | `require("lazy").sync(opts?)`    | Run install, clean and update                                                                                                                        |
+| `:Lazy update [plugins]`  | `require("lazy").update(opts?)`  | Update plugins. This will also update the lockfile                                                                                                   |
 
 <!-- commands:end -->
 
@@ -784,40 +785,40 @@ To uninstall **lazy.nvim**, you need to remove the following files and directori
 
 <!-- colors:start -->
 
-| Highlight Group | Default Group | Description |
-| --- | --- | --- |
-| **LazyButton** | ***CursorLine*** |  |
-| **LazyButtonActive** | ***Visual*** |  |
-| **LazyComment** | ***Comment*** |  |
-| **LazyCommit** | ***@variable.builtin*** | commit ref |
-| **LazyCommitIssue** | ***Number*** |  |
-| **LazyCommitScope** | ***Italic*** | conventional commit scope |
-| **LazyCommitType** | ***Title*** | conventional commit type |
-| **LazyDimmed** | ***Conceal*** | property |
-| **LazyDir** | ***@markup.link*** | directory |
-| **LazyH1** | ***IncSearch*** | home button |
-| **LazyH2** | ***Bold*** | titles |
-| **LazyLocal** | ***Constant*** |  |
-| **LazyNoCond** | ***DiagnosticWarn*** | unloaded icon for a plugin where `cond()` was false |
-| **LazyNormal** | ***NormalFloat*** |  |
-| **LazyProgressDone** | ***Constant*** | progress bar done |
-| **LazyProgressTodo** | ***LineNr*** | progress bar todo |
-| **LazyProp** | ***Conceal*** | property |
-| **LazyReasonCmd** | ***Operator*** |  |
-| **LazyReasonEvent** | ***Constant*** |  |
-| **LazyReasonFt** | ***Character*** |  |
-| **LazyReasonImport** | ***Identifier*** |  |
-| **LazyReasonKeys** | ***Statement*** |  |
-| **LazyReasonPlugin** | ***Special*** |  |
-| **LazyReasonRequire** | ***@variable.parameter*** |  |
-| **LazyReasonRuntime** | ***@macro*** |  |
-| **LazyReasonSource** | ***Character*** |  |
-| **LazyReasonStart** | ***@variable.member*** |  |
-| **LazySpecial** | ***@punctuation.special*** |  |
-| **LazyTaskError** | ***ErrorMsg*** | task errors |
-| **LazyTaskOutput** | ***MsgArea*** | task output |
-| **LazyUrl** | ***@markup.link*** | url |
-| **LazyValue** | ***@string*** | value of a property |
+| Highlight Group       | Default Group              | Description                                         |
+| --------------------- | -------------------------- | --------------------------------------------------- |
+| **LazyButton**        | **_CursorLine_**           |                                                     |
+| **LazyButtonActive**  | **_Visual_**               |                                                     |
+| **LazyComment**       | **_Comment_**              |                                                     |
+| **LazyCommit**        | **_@variable.builtin_**    | commit ref                                          |
+| **LazyCommitIssue**   | **_Number_**               |                                                     |
+| **LazyCommitScope**   | **_Italic_**               | conventional commit scope                           |
+| **LazyCommitType**    | **_Title_**                | conventional commit type                            |
+| **LazyDimmed**        | **_Conceal_**              | property                                            |
+| **LazyDir**           | **_@markup.link_**         | directory                                           |
+| **LazyH1**            | **_IncSearch_**            | home button                                         |
+| **LazyH2**            | **_Bold_**                 | titles                                              |
+| **LazyLocal**         | **_Constant_**             |                                                     |
+| **LazyNoCond**        | **_DiagnosticWarn_**       | unloaded icon for a plugin where `cond()` was false |
+| **LazyNormal**        | **_NormalFloat_**          |                                                     |
+| **LazyProgressDone**  | **_Constant_**             | progress bar done                                   |
+| **LazyProgressTodo**  | **_LineNr_**               | progress bar todo                                   |
+| **LazyProp**          | **_Conceal_**              | property                                            |
+| **LazyReasonCmd**     | **_Operator_**             |                                                     |
+| **LazyReasonEvent**   | **_Constant_**             |                                                     |
+| **LazyReasonFt**      | **_Character_**            |                                                     |
+| **LazyReasonImport**  | **_Identifier_**           |                                                     |
+| **LazyReasonKeys**    | **_Statement_**            |                                                     |
+| **LazyReasonPlugin**  | **_Special_**              |                                                     |
+| **LazyReasonRequire** | **_@variable.parameter_**  |                                                     |
+| **LazyReasonRuntime** | **_@macro_**               |                                                     |
+| **LazyReasonSource**  | **_Character_**            |                                                     |
+| **LazyReasonStart**   | **_@variable.member_**     |                                                     |
+| **LazySpecial**       | **_@punctuation.special_** |                                                     |
+| **LazyTaskError**     | **_ErrorMsg_**             | task errors                                         |
+| **LazyTaskOutput**    | **_MsgArea_**              | task output                                         |
+| **LazyUrl**           | **_@markup.link_**         | url                                                 |
+| **LazyValue**         | **_@string_**              | value of a property                                 |
 
 <!-- colors:end -->
 
diff --git a/lua/lazy/core/config.lua b/lua/lazy/core/config.lua
index a964712..c95931c 100644
--- a/lua/lazy/core/config.lua
+++ b/lua/lazy/core/config.lua
@@ -16,6 +16,7 @@ M.defaults = {
   },
   -- leave nil when passing the spec as the first argument to setup()
   spec = nil, ---@type LazySpec
+  local_spec = true, -- load project specific .lazy.lua spec files. They will be added at the end of the spec.
   lockfile = vim.fn.stdpath("config") .. "/lazy-lock.json", -- lockfile generated after running update.
   ---@type number? limit the maximum amount of concurrent tasks
   concurrency = jit.os:find("Windows") and (vim.uv.available_parallelism() * 2) or nil,
diff --git a/lua/lazy/core/plugin.lua b/lua/lazy/core/plugin.lua
index 64c93cd..7bae395 100644
--- a/lua/lazy/core/plugin.lua
+++ b/lua/lazy/core/plugin.lua
@@ -1,5 +1,4 @@
 local Config = require("lazy.core.config")
-local Handler = require("lazy.core.handler")
 local Util = require("lazy.core.util")
 
 ---@class LazyCorePlugin
@@ -20,6 +19,7 @@ local Spec = {}
 M.Spec = Spec
 M.last_fid = 0
 M.fid_stack = {} ---@type number[]
+M.LOCAL_SPEC = ".lazy.lua"
 
 ---@param spec? LazySpec
 ---@param opts? {optional?:boolean}
@@ -399,10 +399,15 @@ function Spec:import(spec)
 
   ---@type string[]
   local modnames = {}
-  Util.lsmod(spec.import, function(modname)
-    modnames[#modnames + 1] = modname
-  end)
-  table.sort(modnames)
+
+  if spec.import == M.LOCAL_SPEC then
+    modnames = { spec.import }
+  else
+    Util.lsmod(spec.import, function(modname)
+      modnames[#modnames + 1] = modname
+    end)
+    table.sort(modnames)
+  end
 
   for _, modname in ipairs(modnames) do
     imported = imported + 1
@@ -412,7 +417,12 @@ function Spec:import(spec)
     ---@diagnostic disable-next-line: no-unknown
     package.loaded[modname] = nil
     Util.try(function()
-      local mod = require(modname)
+      local mod = nil
+      if modname == M.LOCAL_SPEC then
+        mod = M.local_spec()
+      else
+        mod = require(modname)
+      end
       if type(mod) ~= "table" then
         self.importing = nil
         return self:error(
@@ -533,12 +543,30 @@ function M.update_state()
   end
 end
 
+function M.local_spec()
+  local filepath = vim.fn.fnamemodify(".lazy.lua", ":p")
+  local file = vim.secure.read(filepath)
+  if file then
+    return loadstring(file)()
+  end
+  return {}
+end
+
 function M.load()
   M.loading = true
   -- load specs
   Util.track("spec")
   Config.spec = Spec.new()
-  Config.spec:parse({ vim.deepcopy(Config.options.spec), { "folke/lazy.nvim" } })
+  Config.spec:parse({
+    vim.deepcopy(Config.options.spec),
+    {
+      import = ".lazy.lua",
+      cond = function()
+        return Config.options.local_spec and vim.fn.filereadable(M.LOCAL_SPEC) == 1
+      end,
+    },
+    { "folke/lazy.nvim" },
+  })
 
   -- override some lazy props
   local lazy = Config.spec.plugins["lazy.nvim"]